@agimon-ai/po-artifact 0.0.0-alpha.2 → 0.0.0-alpha.3

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
@@ -3,7 +3,7 @@
3
3
  Always-loaded session artifact storage and web/native views.
4
4
 
5
5
  Each session owns a durable folder under the configured Po agent directory:
6
- `doom-artifacts/<session-id>/`. The agent's system prompt names that folder for
6
+ `po-artifacts/<session-id>/`. The agent's system prompt names that folder for
7
7
  user-designated artifacts and important deliverable documents. Ordinary source
8
8
  edits stay in the project.
9
9
 
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { piAgentDirectory } from "@agimon-ai/po-core/piSettings";
5
5
  //#region src/constants/artifacts.ts
6
- const ARTIFACT_DIRECTORY = "doom-artifacts";
6
+ const ARTIFACT_DIRECTORY = "po-artifacts";
7
7
  const MAX_PREVIEW_BYTES = 1048576;
8
8
  const MAX_CONTENT_BYTES = 26214400;
9
9
  const ARTIFACT_REFRESH_MS = 2e3;
@@ -194,4 +194,4 @@ async function listArtifacts(root) {
194
194
  //#endregion
195
195
  export { ArtifactAccessError as a, MAX_CONTENT_BYTES as c, readArtifactBytes as i, MAX_PREVIEW_BYTES as l, listArtifacts as n, artifactRoot as o, openArtifact as r, ARTIFACT_REFRESH_MS as s, ensureArtifactRoot as t };
196
196
 
197
- //# sourceMappingURL=artifactFiles-B8w_HQXv.mjs.map
197
+ //# sourceMappingURL=artifactFiles-8yvD8PX3.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifactFiles-8yvD8PX3.mjs","names":[],"sources":["../src/constants/artifacts.ts","../src/services/artifactFiles/artifactPaths.ts","../src/services/artifactFiles/index.ts"],"sourcesContent":["export const ARTIFACT_DIRECTORY = 'po-artifacts';\nexport const MAX_PREVIEW_BYTES = 1024 * 1024;\nexport const MAX_CONTENT_BYTES = 25 * 1024 * 1024;\nexport const ARTIFACT_REFRESH_MS = 2000;\n\nexport const ARTIFACT_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/vnd.microsoft.icon',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.m4a': 'audio/mp4',\n '.ogg': 'audio/ogg',\n '.m4v': 'video/mp4',\n '.mov': 'video/quicktime',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.pdf': 'application/pdf',\n '.md': 'text/markdown; charset=utf-8',\n '.markdown': 'text/markdown; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n};\n","import path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport { ARTIFACT_CONTENT_TYPES, ARTIFACT_DIRECTORY } from '../../constants/artifacts';\n\nexport class ArtifactAccessError extends Error {\n constructor(\n message: string,\n readonly status: 400 | 403 | 404 | 413,\n ) {\n super(message);\n }\n}\n\nexport function artifactRoot(sessionId: string, environment: Readonly<Record<string, string | undefined>>): string {\n if (!/^[a-zA-Z0-9_-]+$/u.test(sessionId)) throw new ArtifactAccessError('Invalid artifact session identity.', 400);\n return path.join(piAgentDirectory(environment, environment.HOME), ARTIFACT_DIRECTORY, sessionId);\n}\n\nexport function artifactMimeType(relativePath: string): string {\n return ARTIFACT_CONTENT_TYPES[path.extname(relativePath).toLowerCase()] ?? 'application/octet-stream';\n}\n","import { constants } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { ArtifactItem, ArtifactSnapshot } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactMimeType } from './artifactPaths';\n\nexport { ArtifactAccessError, artifactMimeType, artifactRoot } from './artifactPaths';\n\nfunction inside(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\nfunction codeOf(error: unknown): string | undefined {\n return error instanceof Error && 'code' in error ? String(error.code) : undefined;\n}\n\n/** The owned directories must not redirect artifact access through a symlink. */\nasync function checkedRoot(root: string): Promise<string> {\n const base = path.dirname(root);\n for (const directory of [base, root]) {\n const stat = await fs.lstat(directory);\n if (!stat.isDirectory() || stat.isSymbolicLink())\n throw new ArtifactAccessError('The artifact folder is unavailable.', 403);\n }\n const realBase = await fs.realpath(base);\n const realRoot = await fs.realpath(root);\n if (!inside(realBase, realRoot))\n throw new ArtifactAccessError('The artifact folder is outside its storage directory.', 403);\n return realRoot;\n}\n\nexport async function ensureArtifactRoot(root: string): Promise<void> {\n await fs.mkdir(path.dirname(root), { recursive: true, mode: 0o700 });\n await fs.mkdir(root, { recursive: true, mode: 0o700 });\n await checkedRoot(root);\n}\n\n/** Open without following mutable ancestors. Node exposes no portable openat API. */\nasync function openContained(root: string, segments: string[]): Promise<fs.FileHandle> {\n if (process.platform === 'darwin') {\n // Darwin O_NOFOLLOW_ANY checks every component atomically. Do not combine it with O_NOFOLLOW.\n return fs.open(path.join(root, ...segments), constants.O_RDONLY | constants.O_NONBLOCK | 0x20000000);\n }\n if (process.platform !== 'linux') throw new ArtifactAccessError('Secure artifact reads require macOS or Linux.', 403);\n const flags = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW;\n let directory = await fs.open(path.dirname(root), flags);\n try {\n for (const segment of [path.basename(root), ...segments.slice(0, -1)]) {\n const child = await fs.open(`/proc/self/fd/${directory.fd}/${segment}`, flags);\n await directory.close();\n directory = child;\n }\n return await fs.open(\n `/proc/self/fd/${directory.fd}/${segments.at(-1)!}`,\n constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,\n );\n } finally {\n await directory.close();\n }\n}\n\n/** One guarded open shared by metadata, text and byte reads. The caller closes the handle. */\nexport async function openArtifact(root: string, requested: string | undefined) {\n if (\n !requested ||\n requested.includes('\\0') ||\n requested.includes('\\\\') ||\n path.isAbsolute(requested) ||\n requested.split('/').some((part) => part === '..' || part === '.' || part === '')\n )\n throw new ArtifactAccessError('Invalid artifact path.', 400);\n try {\n const realRoot = await checkedRoot(root);\n const candidate = path.resolve(realRoot, requested);\n if (!inside(realRoot, candidate))\n throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n let current = realRoot;\n const segments = requested.split('/');\n for (let index = 0; index < segments.length; index += 1) {\n current = path.join(current, segments[index]!);\n const stat = await fs.lstat(current);\n if (stat.isSymbolicLink()) throw new ArtifactAccessError('Artifact symlinks cannot be read.', 403);\n if (index < segments.length - 1 ? !stat.isDirectory() : !stat.isFile())\n throw new ArtifactAccessError('The artifact is not a regular file.', 404);\n }\n const real = await fs.realpath(candidate);\n if (!inside(realRoot, real)) throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n const expected = await fs.lstat(real);\n const handle = await openContained(realRoot, segments);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.dev !== expected.dev || stat.ino !== expected.ino)\n throw new ArtifactAccessError('The artifact changed while it was opened. Try again.', 403);\n return { handle, stat, path: requested, mimeType: artifactMimeType(requested) };\n } catch (error) {\n await handle.close();\n throw error;\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError) throw error;\n if (codeOf(error) === 'ENOENT' || codeOf(error) === 'ENOTDIR')\n throw new ArtifactAccessError('The artifact no longer exists.', 404);\n if (codeOf(error) === 'EACCES' || codeOf(error) === 'EPERM' || codeOf(error) === 'ELOOP')\n throw new ArtifactAccessError('The artifact cannot be read.', 403);\n throw error;\n }\n}\n\n/** Bound allocation and read length even when the file grows after its metadata was read. */\nexport async function readArtifactBytes(handle: fs.FileHandle, size: number, offset = 0): Promise<Uint8Array> {\n const bytes = new Uint8Array(size);\n let read = 0;\n while (read < size) {\n const result = await handle.read(bytes, read, size - read, offset + read);\n if (result.bytesRead === 0) break;\n read += result.bytesRead;\n }\n return bytes.subarray(0, read);\n}\n\nexport async function listArtifacts(root: string): Promise<ArtifactSnapshot> {\n const items: ArtifactItem[] = [];\n try {\n const realRoot = await checkedRoot(root);\n const visit = async (directory: string): Promise<void> => {\n for (const entry of await fs.readdir(directory, { withFileTypes: true })) {\n const location = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n // Recheck each directory rather than trusting a readdir entry across a rename.\n const stat = await fs.lstat(location);\n if (!stat.isDirectory() || stat.isSymbolicLink()) continue;\n const real = await fs.realpath(location);\n if (inside(realRoot, real)) await visit(real);\n } else if (entry.isFile()) {\n const relativePath = path.relative(realRoot, location).split(path.sep).join('/');\n try {\n const file = await openArtifact(root, relativePath);\n try {\n items.push({ path: relativePath, name: entry.name, size: file.stat.size, modifiedAt: file.stat.mtimeMs });\n } finally {\n await file.handle.close();\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError && (error.status === 404 || error.status === 403)) continue;\n throw error;\n }\n }\n }\n };\n await visit(realRoot);\n items.sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n return { items };\n } catch (error) {\n if (codeOf(error) === 'ENOENT') return { items: [] };\n return { items: [], error: error instanceof Error ? error.message : 'The artifact folder could not be read.' };\n }\n}\n"],"mappings":"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"}
@@ -27,7 +27,7 @@ let node_path = require("node:path");
27
27
  node_path = __toESM(node_path, 1);
28
28
  let _agimon_ai_po_core_piSettings = require("@agimon-ai/po-core/piSettings");
29
29
  //#region src/constants/artifacts.ts
30
- const ARTIFACT_DIRECTORY = "doom-artifacts";
30
+ const ARTIFACT_DIRECTORY = "po-artifacts";
31
31
  const MAX_PREVIEW_BYTES = 1048576;
32
32
  const MAX_CONTENT_BYTES = 26214400;
33
33
  const ARTIFACT_REFRESH_MS = 2e3;
@@ -277,4 +277,4 @@ Object.defineProperty(exports, "readArtifactBytes", {
277
277
  }
278
278
  });
279
279
 
280
- //# sourceMappingURL=artifactFiles-Bo5Mhwq0.cjs.map
280
+ //# sourceMappingURL=artifactFiles-Cr2X2lhY.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifactFiles-Cr2X2lhY.cjs","names":["path","piAgentDirectory","path","fs","constants"],"sources":["../src/constants/artifacts.ts","../src/services/artifactFiles/artifactPaths.ts","../src/services/artifactFiles/index.ts"],"sourcesContent":["export const ARTIFACT_DIRECTORY = 'po-artifacts';\nexport const MAX_PREVIEW_BYTES = 1024 * 1024;\nexport const MAX_CONTENT_BYTES = 25 * 1024 * 1024;\nexport const ARTIFACT_REFRESH_MS = 2000;\n\nexport const ARTIFACT_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/vnd.microsoft.icon',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.m4a': 'audio/mp4',\n '.ogg': 'audio/ogg',\n '.m4v': 'video/mp4',\n '.mov': 'video/quicktime',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.pdf': 'application/pdf',\n '.md': 'text/markdown; charset=utf-8',\n '.markdown': 'text/markdown; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n};\n","import path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport { ARTIFACT_CONTENT_TYPES, ARTIFACT_DIRECTORY } from '../../constants/artifacts';\n\nexport class ArtifactAccessError extends Error {\n constructor(\n message: string,\n readonly status: 400 | 403 | 404 | 413,\n ) {\n super(message);\n }\n}\n\nexport function artifactRoot(sessionId: string, environment: Readonly<Record<string, string | undefined>>): string {\n if (!/^[a-zA-Z0-9_-]+$/u.test(sessionId)) throw new ArtifactAccessError('Invalid artifact session identity.', 400);\n return path.join(piAgentDirectory(environment, environment.HOME), ARTIFACT_DIRECTORY, sessionId);\n}\n\nexport function artifactMimeType(relativePath: string): string {\n return ARTIFACT_CONTENT_TYPES[path.extname(relativePath).toLowerCase()] ?? 'application/octet-stream';\n}\n","import { constants } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { ArtifactItem, ArtifactSnapshot } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactMimeType } from './artifactPaths';\n\nexport { ArtifactAccessError, artifactMimeType, artifactRoot } from './artifactPaths';\n\nfunction inside(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\nfunction codeOf(error: unknown): string | undefined {\n return error instanceof Error && 'code' in error ? String(error.code) : undefined;\n}\n\n/** The owned directories must not redirect artifact access through a symlink. */\nasync function checkedRoot(root: string): Promise<string> {\n const base = path.dirname(root);\n for (const directory of [base, root]) {\n const stat = await fs.lstat(directory);\n if (!stat.isDirectory() || stat.isSymbolicLink())\n throw new ArtifactAccessError('The artifact folder is unavailable.', 403);\n }\n const realBase = await fs.realpath(base);\n const realRoot = await fs.realpath(root);\n if (!inside(realBase, realRoot))\n throw new ArtifactAccessError('The artifact folder is outside its storage directory.', 403);\n return realRoot;\n}\n\nexport async function ensureArtifactRoot(root: string): Promise<void> {\n await fs.mkdir(path.dirname(root), { recursive: true, mode: 0o700 });\n await fs.mkdir(root, { recursive: true, mode: 0o700 });\n await checkedRoot(root);\n}\n\n/** Open without following mutable ancestors. Node exposes no portable openat API. */\nasync function openContained(root: string, segments: string[]): Promise<fs.FileHandle> {\n if (process.platform === 'darwin') {\n // Darwin O_NOFOLLOW_ANY checks every component atomically. Do not combine it with O_NOFOLLOW.\n return fs.open(path.join(root, ...segments), constants.O_RDONLY | constants.O_NONBLOCK | 0x20000000);\n }\n if (process.platform !== 'linux') throw new ArtifactAccessError('Secure artifact reads require macOS or Linux.', 403);\n const flags = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW;\n let directory = await fs.open(path.dirname(root), flags);\n try {\n for (const segment of [path.basename(root), ...segments.slice(0, -1)]) {\n const child = await fs.open(`/proc/self/fd/${directory.fd}/${segment}`, flags);\n await directory.close();\n directory = child;\n }\n return await fs.open(\n `/proc/self/fd/${directory.fd}/${segments.at(-1)!}`,\n constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,\n );\n } finally {\n await directory.close();\n }\n}\n\n/** One guarded open shared by metadata, text and byte reads. The caller closes the handle. */\nexport async function openArtifact(root: string, requested: string | undefined) {\n if (\n !requested ||\n requested.includes('\\0') ||\n requested.includes('\\\\') ||\n path.isAbsolute(requested) ||\n requested.split('/').some((part) => part === '..' || part === '.' || part === '')\n )\n throw new ArtifactAccessError('Invalid artifact path.', 400);\n try {\n const realRoot = await checkedRoot(root);\n const candidate = path.resolve(realRoot, requested);\n if (!inside(realRoot, candidate))\n throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n let current = realRoot;\n const segments = requested.split('/');\n for (let index = 0; index < segments.length; index += 1) {\n current = path.join(current, segments[index]!);\n const stat = await fs.lstat(current);\n if (stat.isSymbolicLink()) throw new ArtifactAccessError('Artifact symlinks cannot be read.', 403);\n if (index < segments.length - 1 ? !stat.isDirectory() : !stat.isFile())\n throw new ArtifactAccessError('The artifact is not a regular file.', 404);\n }\n const real = await fs.realpath(candidate);\n if (!inside(realRoot, real)) throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n const expected = await fs.lstat(real);\n const handle = await openContained(realRoot, segments);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.dev !== expected.dev || stat.ino !== expected.ino)\n throw new ArtifactAccessError('The artifact changed while it was opened. Try again.', 403);\n return { handle, stat, path: requested, mimeType: artifactMimeType(requested) };\n } catch (error) {\n await handle.close();\n throw error;\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError) throw error;\n if (codeOf(error) === 'ENOENT' || codeOf(error) === 'ENOTDIR')\n throw new ArtifactAccessError('The artifact no longer exists.', 404);\n if (codeOf(error) === 'EACCES' || codeOf(error) === 'EPERM' || codeOf(error) === 'ELOOP')\n throw new ArtifactAccessError('The artifact cannot be read.', 403);\n throw error;\n }\n}\n\n/** Bound allocation and read length even when the file grows after its metadata was read. */\nexport async function readArtifactBytes(handle: fs.FileHandle, size: number, offset = 0): Promise<Uint8Array> {\n const bytes = new Uint8Array(size);\n let read = 0;\n while (read < size) {\n const result = await handle.read(bytes, read, size - read, offset + read);\n if (result.bytesRead === 0) break;\n read += result.bytesRead;\n }\n return bytes.subarray(0, read);\n}\n\nexport async function listArtifacts(root: string): Promise<ArtifactSnapshot> {\n const items: ArtifactItem[] = [];\n try {\n const realRoot = await checkedRoot(root);\n const visit = async (directory: string): Promise<void> => {\n for (const entry of await fs.readdir(directory, { withFileTypes: true })) {\n const location = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n // Recheck each directory rather than trusting a readdir entry across a rename.\n const stat = await fs.lstat(location);\n if (!stat.isDirectory() || stat.isSymbolicLink()) continue;\n const real = await fs.realpath(location);\n if (inside(realRoot, real)) await visit(real);\n } else if (entry.isFile()) {\n const relativePath = path.relative(realRoot, location).split(path.sep).join('/');\n try {\n const file = await openArtifact(root, relativePath);\n try {\n items.push({ path: relativePath, name: entry.name, size: file.stat.size, modifiedAt: file.stat.mtimeMs });\n } finally {\n await file.handle.close();\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError && (error.status === 404 || error.status === 403)) continue;\n throw error;\n }\n }\n }\n };\n await visit(realRoot);\n items.sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n return { items };\n } catch (error) {\n if (codeOf(error) === 'ENOENT') return { items: [] };\n return { items: [], error: error instanceof Error ? error.message : 'The artifact folder could not be read.' };\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_artifactFiles = require("./artifactFiles-Bo5Mhwq0.cjs");
2
+ const require_artifactFiles = require("./artifactFiles-Cr2X2lhY.cjs");
3
3
  exports.ArtifactAccessError = require_artifactFiles.ArtifactAccessError;
4
4
  exports.artifactRoot = require_artifactFiles.artifactRoot;
5
5
  exports.openArtifact = require_artifactFiles.openArtifact;
@@ -1 +1 @@
1
- export { a as ArtifactAccessError, o as artifactRoot, r as openArtifact, i as readArtifactBytes } from "./artifactFiles-B8w_HQXv.mjs";
1
+ export { a as ArtifactAccessError, o as artifactRoot, r as openArtifact, i as readArtifactBytes } from "./artifactFiles-8yvD8PX3.mjs";
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_artifactFiles = require("../artifactFiles-Bo5Mhwq0.cjs");
5
+ const require_artifactFiles = require("../artifactFiles-Cr2X2lhY.cjs");
6
6
  let node_path = require("node:path");
7
7
  node_path = require_artifactFiles.__toESM(node_path, 1);
8
8
  let _agimon_ai_po_core_serverFacet = require("@agimon-ai/po-core/serverFacet");
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","names":["ensureArtifactRoot","ArtifactAccessError","listArtifacts","setTimeout","ARTIFACT_REFRESH_MS","artifactRoot","defineRoot","listArtifacts","artifactRoot","defineChannel","Hono","ArtifactAccessError","listArtifacts","openArtifact","readArtifactBytes","MAX_PREVIEW_BYTES","path","artifactRoot","defineRoute","defineHook","defineHook","defineServerPlugin","composeServerProviderOverrides","rootSession","channelArtifacts","composeRootHooks","apiRoute","hookBeforeAgentStart","hookToolExecutionEnd"],"sources":["../../src/types/artifacts.ts","../../src/services/artifactSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/artifactChannel/index.ts","../../src/extensions/(backend)/channel/artifacts.server.ts","../../src/types/apiRoutes.ts","../../src/services/artifactsApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts","../../generated/server.ts"],"sourcesContent":["export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import { clearTimeout, setTimeout } from 'node:timers';\n\nimport type { DoomHeadlessActivity, DoomHeadlessExecutionContext, DoomHeadlessHook } from '@agimon-ai/po-core/headless';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { ARTIFACT_REFRESH_MS } from '../../constants/artifacts';\nimport { artifactsChannelType } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, ensureArtifactRoot, listArtifacts } from '../artifactFiles';\n\n/**\n * Creates the session artifact folder. Resolves false when the host has no artifact folder\n * (the edge build in M1 rejects with a 404); Node never rejects with that status here.\n */\nasync function prepareArtifactRoot(root: string): Promise<boolean> {\n try {\n await ensureArtifactRoot(root);\n return true;\n } catch (error) {\n if (error instanceof ArtifactAccessError && error.status === 404) return false;\n throw error;\n }\n}\n\nexport function createArtifactSession({ host }: DoomServerPluginContext) {\n const environment = Object.freeze({ ...host.context.environment });\n let active: { context: DoomHeadlessExecutionContext; root: string; fingerprint?: string } | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: Promise<void> | undefined;\n let closed = false;\n\n const refresh = (): Promise<void> => {\n if (pending) return pending;\n const runtime = active;\n if (!runtime || closed) return Promise.resolve();\n pending = (async () => {\n const snapshot = await listArtifacts(runtime.root);\n if (closed || active !== runtime) return;\n const fingerprint = JSON.stringify(snapshot);\n if (fingerprint === runtime.fingerprint) return;\n runtime.fingerprint = fingerprint;\n host.context.directEvents?.publish(artifactsChannelType, runtime.context.sessionId, snapshot);\n })().finally(() => {\n pending = undefined;\n });\n return pending;\n };\n const schedule = (): void => {\n if (!active || closed) return;\n timer = setTimeout(() => {\n timer = undefined;\n void refresh().finally(schedule);\n }, ARTIFACT_REFRESH_MS);\n };\n const stop = async (): Promise<void> => {\n active = undefined;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n await pending;\n };\n const activity: DoomHeadlessActivity = {\n name: 'artifacts',\n async start(context) {\n if (closed) throw new Error('Artifact session is closed.');\n await stop();\n const root = artifactRoot(context.sessionId, environment);\n if (!(await prepareArtifactRoot(root))) return stop;\n if (closed) throw new Error('Artifact session is closed.');\n active = { context, root };\n await refresh();\n schedule();\n return stop;\n },\n };\n const beforeAgentStart: DoomHeadlessHook = {\n event: 'before_agent_start',\n async handle(event, context) {\n const root = artifactRoot(context.sessionId, environment);\n const systemPrompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n if (!(await prepareArtifactRoot(root))) return { systemPrompt };\n const declaration = `Session artifact folder: ${root}`;\n if (systemPrompt.includes(declaration)) return { systemPrompt };\n return {\n systemPrompt:\n `${systemPrompt}\\n\\n${declaration}\\nSave user-designated artifacts and important deliverable documents in this folder, using descriptive filenames. Keep ordinary repository source edits in their existing locations. Do not move existing documents or overwrite artifacts without the user's intent. This folder belongs only to the current session.`.trim(),\n };\n },\n };\n const toolExecutionEnd: DoomHeadlessHook = {\n event: 'tool_execution_end',\n handle(_event, context) {\n return active?.context.sessionId === context.sessionId ? refresh() : undefined;\n },\n };\n return {\n activities: [activity],\n beforeAgentStart,\n toolExecutionEnd,\n onDispose: async () => {\n closed = true;\n await stop();\n },\n };\n}\n","import type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createArtifactSession } from '../../../../../services/artifactSession';\n\nexport const createArtifactServerRoot = (context: DoomServerPluginContext) => {\n const runtime = createArtifactSession(context);\n return { value: runtime, activities: runtime.activities, onDispose: runtime.onDispose };\n};\nexport type ArtifactServerScope = ReturnType<typeof createArtifactServerRoot>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createArtifactServerRoot);\n","import type { DoomHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../types/artifacts';\nimport { artifactRoot, listArtifacts } from '../artifactFiles';\n\nfunction snapshotOf(value: unknown): value is ArtifactSnapshot {\n if (typeof value !== 'object' || value === null) return false;\n const snapshot = value as Partial<ArtifactSnapshot>;\n return (\n Array.isArray(snapshot.items) &&\n (snapshot.error === undefined || typeof snapshot.error === 'string') &&\n snapshot.items.every(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n typeof item.path === 'string' &&\n typeof item.name === 'string' &&\n typeof item.size === 'number' &&\n Number.isFinite(item.size) &&\n typeof item.modifiedAt === 'number' &&\n Number.isFinite(item.modifiedAt),\n )\n );\n}\n\nexport function createArtifactsChannel(): DoomHubChannel {\n return {\n frameType: artifactsChannelType,\n start(host) {\n const sessions = new Map<string, { snapshot?: ArtifactSnapshot; unsubscribe: () => void }>();\n return {\n payloadFor(scope) {\n return sessions.get(scope.sessionId)?.snapshot;\n },\n sessionAdded(scope) {\n sessions.get(scope.sessionId)?.unsubscribe();\n const state: { snapshot?: ArtifactSnapshot; unsubscribe: () => void } = { unsubscribe: () => undefined };\n sessions.set(scope.sessionId, state);\n state.unsubscribe = host.directEvents.subscribe(\n artifactsChannelType,\n scope.sessionId,\n (snapshot) => {\n if (!snapshotOf(snapshot) || sessions.get(scope.sessionId) !== state) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n },\n { replayLatest: true },\n );\n void (async () => {\n let snapshot: ArtifactSnapshot;\n try {\n snapshot = await listArtifacts(artifactRoot(scope.sessionId, scope.environment ?? {}));\n } catch (error) {\n snapshot = {\n items: [],\n error: error instanceof Error ? error.message : 'The artifact folder could not be read.',\n };\n }\n // A live session event wins over the earlier seed read.\n if (sessions.get(scope.sessionId) !== state || state.snapshot !== undefined) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n })();\n },\n sessionRemoved(sessionId) {\n sessions.get(sessionId)?.unsubscribe();\n sessions.delete(sessionId);\n },\n close() {\n for (const state of sessions.values()) state.unsubscribe();\n sessions.clear();\n },\n };\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactsChannel } from '../../../services/artifactChannel';\n\nexport default defineChannel(createArtifactsChannel);\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import path from 'node:path';\n\nimport type { DoomApi, DoomApiContext } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { MAX_CONTENT_BYTES, MAX_PREVIEW_BYTES } from '../../constants/artifacts';\nimport routes from '../../types/apiRoutes';\nimport type { ArtifactPreview } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, listArtifacts, openArtifact, readArtifactBytes } from '../artifactFiles';\n\nexport function createArtifactsApi(root: string): Hono {\n const app = new Hono();\n app.onError((error, context) =>\n context.json(\n { error: error instanceof ArtifactAccessError ? error.message : 'The artifact could not be read.' },\n error instanceof ArtifactAccessError ? error.status : 500,\n ),\n );\n app.get(routes.list.path, async (context) => {\n const snapshot = await listArtifacts(root);\n return context.json(snapshot, snapshot.error === undefined ? 200 : 500);\n });\n app.get(routes.preview.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n const preview: ArtifactPreview = { path: file.path, content: '', mimeType: file.mimeType };\n if (file.stat.size > MAX_PREVIEW_BYTES) {\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n }\n const bytes = await readArtifactBytes(file.handle, MAX_PREVIEW_BYTES + 1);\n if (bytes.byteLength > MAX_PREVIEW_BYTES)\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n if (bytes.subarray(0, 8192).includes(0))\n return context.json({ ...preview, unavailable: true, reason: 'This artifact is binary.' });\n return context.json({ ...preview, content: new TextDecoder().decode(bytes) });\n } finally {\n await file.handle.close();\n }\n });\n app.get(routes.content.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n if (file.stat.size > MAX_CONTENT_BYTES)\n throw new ArtifactAccessError('This artifact is too large to download.', 413);\n const version = `\"${file.stat.dev}-${file.stat.ino}-${file.stat.size}-${file.stat.mtimeMs}-${file.stat.ctimeMs}\"`;\n const name = encodeURIComponent(path.basename(file.path)).replace(\n /[!'()*]/gu,\n (value) => `%${value.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n const headers = new Headers({\n 'content-type': file.mimeType,\n 'content-disposition': `attachment; filename*=UTF-8''${name}`,\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n 'content-security-policy': \"default-src 'none'; sandbox\",\n 'accept-ranges': 'bytes',\n etag: version,\n });\n const expectedVersion = context.req.header('if-match');\n if (expectedVersion !== undefined && expectedVersion !== version)\n return new Response(null, { status: 412, headers });\n let start = 0;\n let end = file.stat.size - 1;\n const range = context.req.header('range');\n if (range !== undefined) {\n const match = /^bytes=(\\d*)-(\\d*)$/u.exec(range);\n if (\n !match ||\n (!match[1] && !match[2]) ||\n file.stat.size === 0 ||\n [match[1], match[2]].some((value) => value !== '' && !Number.isSafeInteger(Number(value)))\n ) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n if (match[1] === '') {\n const suffix = Number(match[2]);\n start = suffix > 0 ? Math.max(0, file.stat.size - suffix) : file.stat.size;\n } else {\n start = Number(match[1]);\n end = match[2] === '' ? end : Math.min(end, Number(match[2]));\n }\n if (start > end || start >= file.stat.size) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set('content-range', `bytes ${start}-${end}/${file.stat.size}`);\n }\n const bytes = await readArtifactBytes(file.handle, Math.max(0, end - start + 1), start);\n const after = await file.handle.stat();\n if (after.size !== file.stat.size || after.mtimeMs !== file.stat.mtimeMs || after.ctimeMs !== file.stat.ctimeMs)\n return new Response(null, { status: 412, headers });\n headers.set('content-length', String(bytes.byteLength));\n return new Response(bytes, { status: range === undefined ? 200 : 206, headers });\n } finally {\n await file.handle.close();\n }\n });\n return app;\n}\n\nexport const api: DoomApi = {\n basePath: 'artifacts',\n start(context: DoomApiContext) {\n if (!context.sessionId) throw new Error('Artifacts requires a session identity.');\n const app = createArtifactsApi(artifactRoot(context.sessionId, context.environment ?? {}));\n return { fetch: (request) => app.fetch(request), close: () => undefined };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/artifactsApi';\n\nexport default defineRoute(api);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<DoomServerPluginContext, ArtifactServerScope>) => context.root.beforeAgentStart,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<DoomServerPluginContext, ArtifactServerScope>) => context.root.toolExecutionEnd,\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelArtifacts from '../src/extensions/(backend)/channel/artifacts.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server';\nimport hookToolExecutionEnd from '../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-artifact',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: () => ({\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n workspace: () => ({\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n get hooks(): DoomServerSessionPlugin['hooks'] { return [via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'tool_execution_end' }, at(hookToolExecutionEnd, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.cjs","names":["ensureArtifactRoot","ArtifactAccessError","listArtifacts","setTimeout","ARTIFACT_REFRESH_MS","artifactRoot","defineRoot","listArtifacts","artifactRoot","defineChannel","Hono","ArtifactAccessError","listArtifacts","openArtifact","readArtifactBytes","MAX_PREVIEW_BYTES","path","artifactRoot","defineRoute","defineHook","defineHook","defineServerPlugin","composeServerProviderOverrides","rootSession","channelArtifacts","composeRootHooks","apiRoute","hookBeforeAgentStart","hookToolExecutionEnd"],"sources":["../../src/types/artifacts.ts","../../src/services/artifactSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/artifactChannel/index.ts","../../src/extensions/(backend)/channel/artifacts.server.ts","../../src/types/apiRoutes.ts","../../src/services/artifactsApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts","../../generated/server.ts"],"sourcesContent":["export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import { clearTimeout, setTimeout } from 'node:timers';\n\nimport type { PoHeadlessActivity, PoHeadlessExecutionContext, PoHeadlessHook } from '@agimon-ai/po-core/headless';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { ARTIFACT_REFRESH_MS } from '../../constants/artifacts';\nimport { artifactsChannelType } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, ensureArtifactRoot, listArtifacts } from '../artifactFiles';\n\n/**\n * Creates the session artifact folder. Resolves false when the host has no artifact folder\n * (the edge build in M1 rejects with a 404); Node never rejects with that status here.\n */\nasync function prepareArtifactRoot(root: string): Promise<boolean> {\n try {\n await ensureArtifactRoot(root);\n return true;\n } catch (error) {\n if (error instanceof ArtifactAccessError && error.status === 404) return false;\n throw error;\n }\n}\n\nexport function createArtifactSession({ host }: PoServerPluginContext) {\n const environment = Object.freeze({ ...host.context.environment });\n let active: { context: PoHeadlessExecutionContext; root: string; fingerprint?: string } | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: Promise<void> | undefined;\n let closed = false;\n\n const refresh = (): Promise<void> => {\n if (pending) return pending;\n const runtime = active;\n if (!runtime || closed) return Promise.resolve();\n pending = (async () => {\n const snapshot = await listArtifacts(runtime.root);\n if (closed || active !== runtime) return;\n const fingerprint = JSON.stringify(snapshot);\n if (fingerprint === runtime.fingerprint) return;\n runtime.fingerprint = fingerprint;\n host.context.directEvents?.publish(artifactsChannelType, runtime.context.sessionId, snapshot);\n })().finally(() => {\n pending = undefined;\n });\n return pending;\n };\n const schedule = (): void => {\n if (!active || closed) return;\n timer = setTimeout(() => {\n timer = undefined;\n void refresh().finally(schedule);\n }, ARTIFACT_REFRESH_MS);\n };\n const stop = async (): Promise<void> => {\n active = undefined;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n await pending;\n };\n const activity: PoHeadlessActivity = {\n name: 'artifacts',\n async start(context) {\n if (closed) throw new Error('Artifact session is closed.');\n await stop();\n const root = artifactRoot(context.sessionId, environment);\n if (!(await prepareArtifactRoot(root))) return stop;\n if (closed) throw new Error('Artifact session is closed.');\n active = { context, root };\n await refresh();\n schedule();\n return stop;\n },\n };\n const beforeAgentStart: PoHeadlessHook = {\n event: 'before_agent_start',\n async handle(event, context) {\n const root = artifactRoot(context.sessionId, environment);\n const systemPrompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n if (!(await prepareArtifactRoot(root))) return { systemPrompt };\n const declaration = `Session artifact folder: ${root}`;\n if (systemPrompt.includes(declaration)) return { systemPrompt };\n return {\n systemPrompt:\n `${systemPrompt}\\n\\n${declaration}\\nSave user-designated artifacts and important deliverable documents in this folder, using descriptive filenames. Keep ordinary repository source edits in their existing locations. Do not move existing documents or overwrite artifacts without the user's intent. This folder belongs only to the current session.`.trim(),\n };\n },\n };\n const toolExecutionEnd: PoHeadlessHook = {\n event: 'tool_execution_end',\n handle(_event, context) {\n return active?.context.sessionId === context.sessionId ? refresh() : undefined;\n },\n };\n return {\n activities: [activity],\n beforeAgentStart,\n toolExecutionEnd,\n onDispose: async () => {\n closed = true;\n await stop();\n },\n };\n}\n","import type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createArtifactSession } from '../../../../../services/artifactSession';\n\nexport const createArtifactServerRoot = (context: PoServerPluginContext) => {\n const runtime = createArtifactSession(context);\n return { value: runtime, activities: runtime.activities, onDispose: runtime.onDispose };\n};\nexport type ArtifactServerScope = ReturnType<typeof createArtifactServerRoot>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createArtifactServerRoot);\n","import type { PoHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../types/artifacts';\nimport { artifactRoot, listArtifacts } from '../artifactFiles';\n\nfunction snapshotOf(value: unknown): value is ArtifactSnapshot {\n if (typeof value !== 'object' || value === null) return false;\n const snapshot = value as Partial<ArtifactSnapshot>;\n return (\n Array.isArray(snapshot.items) &&\n (snapshot.error === undefined || typeof snapshot.error === 'string') &&\n snapshot.items.every(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n typeof item.path === 'string' &&\n typeof item.name === 'string' &&\n typeof item.size === 'number' &&\n Number.isFinite(item.size) &&\n typeof item.modifiedAt === 'number' &&\n Number.isFinite(item.modifiedAt),\n )\n );\n}\n\nexport function createArtifactsChannel(): PoHubChannel {\n return {\n frameType: artifactsChannelType,\n start(host) {\n const sessions = new Map<string, { snapshot?: ArtifactSnapshot; unsubscribe: () => void }>();\n return {\n payloadFor(scope) {\n return sessions.get(scope.sessionId)?.snapshot;\n },\n sessionAdded(scope) {\n sessions.get(scope.sessionId)?.unsubscribe();\n const state: { snapshot?: ArtifactSnapshot; unsubscribe: () => void } = { unsubscribe: () => undefined };\n sessions.set(scope.sessionId, state);\n state.unsubscribe = host.directEvents.subscribe(\n artifactsChannelType,\n scope.sessionId,\n (snapshot) => {\n if (!snapshotOf(snapshot) || sessions.get(scope.sessionId) !== state) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n },\n { replayLatest: true },\n );\n void (async () => {\n let snapshot: ArtifactSnapshot;\n try {\n snapshot = await listArtifacts(artifactRoot(scope.sessionId, scope.environment ?? {}));\n } catch (error) {\n snapshot = {\n items: [],\n error: error instanceof Error ? error.message : 'The artifact folder could not be read.',\n };\n }\n // A live session event wins over the earlier seed read.\n if (sessions.get(scope.sessionId) !== state || state.snapshot !== undefined) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n })();\n },\n sessionRemoved(sessionId) {\n sessions.get(sessionId)?.unsubscribe();\n sessions.delete(sessionId);\n },\n close() {\n for (const state of sessions.values()) state.unsubscribe();\n sessions.clear();\n },\n };\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactsChannel } from '../../../services/artifactChannel';\n\nexport default defineChannel(createArtifactsChannel);\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import path from 'node:path';\n\nimport type { PoApi, PoApiContext } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { MAX_CONTENT_BYTES, MAX_PREVIEW_BYTES } from '../../constants/artifacts';\nimport routes from '../../types/apiRoutes';\nimport type { ArtifactPreview } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, listArtifacts, openArtifact, readArtifactBytes } from '../artifactFiles';\n\nexport function createArtifactsApi(root: string): Hono {\n const app = new Hono();\n app.onError((error, context) =>\n context.json(\n { error: error instanceof ArtifactAccessError ? error.message : 'The artifact could not be read.' },\n error instanceof ArtifactAccessError ? error.status : 500,\n ),\n );\n app.get(routes.list.path, async (context) => {\n const snapshot = await listArtifacts(root);\n return context.json(snapshot, snapshot.error === undefined ? 200 : 500);\n });\n app.get(routes.preview.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n const preview: ArtifactPreview = { path: file.path, content: '', mimeType: file.mimeType };\n if (file.stat.size > MAX_PREVIEW_BYTES) {\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n }\n const bytes = await readArtifactBytes(file.handle, MAX_PREVIEW_BYTES + 1);\n if (bytes.byteLength > MAX_PREVIEW_BYTES)\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n if (bytes.subarray(0, 8192).includes(0))\n return context.json({ ...preview, unavailable: true, reason: 'This artifact is binary.' });\n return context.json({ ...preview, content: new TextDecoder().decode(bytes) });\n } finally {\n await file.handle.close();\n }\n });\n app.get(routes.content.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n if (file.stat.size > MAX_CONTENT_BYTES)\n throw new ArtifactAccessError('This artifact is too large to download.', 413);\n const version = `\"${file.stat.dev}-${file.stat.ino}-${file.stat.size}-${file.stat.mtimeMs}-${file.stat.ctimeMs}\"`;\n const name = encodeURIComponent(path.basename(file.path)).replace(\n /[!'()*]/gu,\n (value) => `%${value.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n const headers = new Headers({\n 'content-type': file.mimeType,\n 'content-disposition': `attachment; filename*=UTF-8''${name}`,\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n 'content-security-policy': \"default-src 'none'; sandbox\",\n 'accept-ranges': 'bytes',\n etag: version,\n });\n const expectedVersion = context.req.header('if-match');\n if (expectedVersion !== undefined && expectedVersion !== version)\n return new Response(null, { status: 412, headers });\n let start = 0;\n let end = file.stat.size - 1;\n const range = context.req.header('range');\n if (range !== undefined) {\n const match = /^bytes=(\\d*)-(\\d*)$/u.exec(range);\n if (\n !match ||\n (!match[1] && !match[2]) ||\n file.stat.size === 0 ||\n [match[1], match[2]].some((value) => value !== '' && !Number.isSafeInteger(Number(value)))\n ) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n if (match[1] === '') {\n const suffix = Number(match[2]);\n start = suffix > 0 ? Math.max(0, file.stat.size - suffix) : file.stat.size;\n } else {\n start = Number(match[1]);\n end = match[2] === '' ? end : Math.min(end, Number(match[2]));\n }\n if (start > end || start >= file.stat.size) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set('content-range', `bytes ${start}-${end}/${file.stat.size}`);\n }\n const bytes = await readArtifactBytes(file.handle, Math.max(0, end - start + 1), start);\n const after = await file.handle.stat();\n if (after.size !== file.stat.size || after.mtimeMs !== file.stat.mtimeMs || after.ctimeMs !== file.stat.ctimeMs)\n return new Response(null, { status: 412, headers });\n headers.set('content-length', String(bytes.byteLength));\n return new Response(bytes, { status: range === undefined ? 200 : 206, headers });\n } finally {\n await file.handle.close();\n }\n });\n return app;\n}\n\nexport const api: PoApi = {\n basePath: 'artifacts',\n start(context: PoApiContext) {\n if (!context.sessionId) throw new Error('Artifacts requires a session identity.');\n const app = createArtifactsApi(artifactRoot(context.sessionId, context.environment ?? {}));\n return { fetch: (request) => app.fetch(request), close: () => undefined };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/artifactsApi';\n\nexport default defineRoute(api);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<PoServerPluginContext, ArtifactServerScope>) => context.root.beforeAgentStart,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<PoServerPluginContext, ArtifactServerScope>) => context.root.toolExecutionEnd,\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelArtifacts from '../src/extensions/(backend)/channel/artifacts.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server';\nimport hookToolExecutionEnd from '../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-artifact',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: () => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n workspace: () => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return [via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'tool_execution_end' }, at(hookToolExecutionEnd, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  //#region generated/server.d.ts
2
- export declare const facet: import("@agimon-ai/po-core/serverFacet").DoomServerFacet;
2
+ export declare const facet: import("@agimon-ai/po-core/serverFacet").PoServerFacet;
3
3
  //#endregion
4
4
  export { facet as default };
5
5
  //# sourceMappingURL=server.d.mts.map
@@ -1,4 +1,4 @@
1
- import { a as ArtifactAccessError, i as readArtifactBytes, l as MAX_PREVIEW_BYTES, n as listArtifacts, o as artifactRoot, r as openArtifact, s as ARTIFACT_REFRESH_MS, t as ensureArtifactRoot } from "../artifactFiles-B8w_HQXv.mjs";
1
+ import { a as ArtifactAccessError, i as readArtifactBytes, l as MAX_PREVIEW_BYTES, n as listArtifacts, o as artifactRoot, r as openArtifact, s as ARTIFACT_REFRESH_MS, t as ensureArtifactRoot } from "../artifactFiles-8yvD8PX3.mjs";
2
2
  import path from "node:path";
3
3
  import { composeServerProviderOverrides, defineServerPlugin } from "@agimon-ai/po-core/serverFacet";
4
4
  import { composeRootHooks, defineChannel, defineHook, defineRoot, defineRoute } from "@agimon-ai/po-core/extensionFile";
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":["rootSession","channelArtifacts","apiRoute","hookBeforeAgentStart","hookToolExecutionEnd"],"sources":["../../src/types/artifacts.ts","../../src/services/artifactSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/artifactChannel/index.ts","../../src/extensions/(backend)/channel/artifacts.server.ts","../../src/types/apiRoutes.ts","../../src/services/artifactsApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts","../../generated/server.ts"],"sourcesContent":["export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import { clearTimeout, setTimeout } from 'node:timers';\n\nimport type { DoomHeadlessActivity, DoomHeadlessExecutionContext, DoomHeadlessHook } from '@agimon-ai/po-core/headless';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { ARTIFACT_REFRESH_MS } from '../../constants/artifacts';\nimport { artifactsChannelType } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, ensureArtifactRoot, listArtifacts } from '../artifactFiles';\n\n/**\n * Creates the session artifact folder. Resolves false when the host has no artifact folder\n * (the edge build in M1 rejects with a 404); Node never rejects with that status here.\n */\nasync function prepareArtifactRoot(root: string): Promise<boolean> {\n try {\n await ensureArtifactRoot(root);\n return true;\n } catch (error) {\n if (error instanceof ArtifactAccessError && error.status === 404) return false;\n throw error;\n }\n}\n\nexport function createArtifactSession({ host }: DoomServerPluginContext) {\n const environment = Object.freeze({ ...host.context.environment });\n let active: { context: DoomHeadlessExecutionContext; root: string; fingerprint?: string } | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: Promise<void> | undefined;\n let closed = false;\n\n const refresh = (): Promise<void> => {\n if (pending) return pending;\n const runtime = active;\n if (!runtime || closed) return Promise.resolve();\n pending = (async () => {\n const snapshot = await listArtifacts(runtime.root);\n if (closed || active !== runtime) return;\n const fingerprint = JSON.stringify(snapshot);\n if (fingerprint === runtime.fingerprint) return;\n runtime.fingerprint = fingerprint;\n host.context.directEvents?.publish(artifactsChannelType, runtime.context.sessionId, snapshot);\n })().finally(() => {\n pending = undefined;\n });\n return pending;\n };\n const schedule = (): void => {\n if (!active || closed) return;\n timer = setTimeout(() => {\n timer = undefined;\n void refresh().finally(schedule);\n }, ARTIFACT_REFRESH_MS);\n };\n const stop = async (): Promise<void> => {\n active = undefined;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n await pending;\n };\n const activity: DoomHeadlessActivity = {\n name: 'artifacts',\n async start(context) {\n if (closed) throw new Error('Artifact session is closed.');\n await stop();\n const root = artifactRoot(context.sessionId, environment);\n if (!(await prepareArtifactRoot(root))) return stop;\n if (closed) throw new Error('Artifact session is closed.');\n active = { context, root };\n await refresh();\n schedule();\n return stop;\n },\n };\n const beforeAgentStart: DoomHeadlessHook = {\n event: 'before_agent_start',\n async handle(event, context) {\n const root = artifactRoot(context.sessionId, environment);\n const systemPrompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n if (!(await prepareArtifactRoot(root))) return { systemPrompt };\n const declaration = `Session artifact folder: ${root}`;\n if (systemPrompt.includes(declaration)) return { systemPrompt };\n return {\n systemPrompt:\n `${systemPrompt}\\n\\n${declaration}\\nSave user-designated artifacts and important deliverable documents in this folder, using descriptive filenames. Keep ordinary repository source edits in their existing locations. Do not move existing documents or overwrite artifacts without the user's intent. This folder belongs only to the current session.`.trim(),\n };\n },\n };\n const toolExecutionEnd: DoomHeadlessHook = {\n event: 'tool_execution_end',\n handle(_event, context) {\n return active?.context.sessionId === context.sessionId ? refresh() : undefined;\n },\n };\n return {\n activities: [activity],\n beforeAgentStart,\n toolExecutionEnd,\n onDispose: async () => {\n closed = true;\n await stop();\n },\n };\n}\n","import type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createArtifactSession } from '../../../../../services/artifactSession';\n\nexport const createArtifactServerRoot = (context: DoomServerPluginContext) => {\n const runtime = createArtifactSession(context);\n return { value: runtime, activities: runtime.activities, onDispose: runtime.onDispose };\n};\nexport type ArtifactServerScope = ReturnType<typeof createArtifactServerRoot>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createArtifactServerRoot);\n","import type { DoomHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../types/artifacts';\nimport { artifactRoot, listArtifacts } from '../artifactFiles';\n\nfunction snapshotOf(value: unknown): value is ArtifactSnapshot {\n if (typeof value !== 'object' || value === null) return false;\n const snapshot = value as Partial<ArtifactSnapshot>;\n return (\n Array.isArray(snapshot.items) &&\n (snapshot.error === undefined || typeof snapshot.error === 'string') &&\n snapshot.items.every(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n typeof item.path === 'string' &&\n typeof item.name === 'string' &&\n typeof item.size === 'number' &&\n Number.isFinite(item.size) &&\n typeof item.modifiedAt === 'number' &&\n Number.isFinite(item.modifiedAt),\n )\n );\n}\n\nexport function createArtifactsChannel(): DoomHubChannel {\n return {\n frameType: artifactsChannelType,\n start(host) {\n const sessions = new Map<string, { snapshot?: ArtifactSnapshot; unsubscribe: () => void }>();\n return {\n payloadFor(scope) {\n return sessions.get(scope.sessionId)?.snapshot;\n },\n sessionAdded(scope) {\n sessions.get(scope.sessionId)?.unsubscribe();\n const state: { snapshot?: ArtifactSnapshot; unsubscribe: () => void } = { unsubscribe: () => undefined };\n sessions.set(scope.sessionId, state);\n state.unsubscribe = host.directEvents.subscribe(\n artifactsChannelType,\n scope.sessionId,\n (snapshot) => {\n if (!snapshotOf(snapshot) || sessions.get(scope.sessionId) !== state) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n },\n { replayLatest: true },\n );\n void (async () => {\n let snapshot: ArtifactSnapshot;\n try {\n snapshot = await listArtifacts(artifactRoot(scope.sessionId, scope.environment ?? {}));\n } catch (error) {\n snapshot = {\n items: [],\n error: error instanceof Error ? error.message : 'The artifact folder could not be read.',\n };\n }\n // A live session event wins over the earlier seed read.\n if (sessions.get(scope.sessionId) !== state || state.snapshot !== undefined) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n })();\n },\n sessionRemoved(sessionId) {\n sessions.get(sessionId)?.unsubscribe();\n sessions.delete(sessionId);\n },\n close() {\n for (const state of sessions.values()) state.unsubscribe();\n sessions.clear();\n },\n };\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactsChannel } from '../../../services/artifactChannel';\n\nexport default defineChannel(createArtifactsChannel);\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import path from 'node:path';\n\nimport type { DoomApi, DoomApiContext } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { MAX_CONTENT_BYTES, MAX_PREVIEW_BYTES } from '../../constants/artifacts';\nimport routes from '../../types/apiRoutes';\nimport type { ArtifactPreview } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, listArtifacts, openArtifact, readArtifactBytes } from '../artifactFiles';\n\nexport function createArtifactsApi(root: string): Hono {\n const app = new Hono();\n app.onError((error, context) =>\n context.json(\n { error: error instanceof ArtifactAccessError ? error.message : 'The artifact could not be read.' },\n error instanceof ArtifactAccessError ? error.status : 500,\n ),\n );\n app.get(routes.list.path, async (context) => {\n const snapshot = await listArtifacts(root);\n return context.json(snapshot, snapshot.error === undefined ? 200 : 500);\n });\n app.get(routes.preview.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n const preview: ArtifactPreview = { path: file.path, content: '', mimeType: file.mimeType };\n if (file.stat.size > MAX_PREVIEW_BYTES) {\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n }\n const bytes = await readArtifactBytes(file.handle, MAX_PREVIEW_BYTES + 1);\n if (bytes.byteLength > MAX_PREVIEW_BYTES)\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n if (bytes.subarray(0, 8192).includes(0))\n return context.json({ ...preview, unavailable: true, reason: 'This artifact is binary.' });\n return context.json({ ...preview, content: new TextDecoder().decode(bytes) });\n } finally {\n await file.handle.close();\n }\n });\n app.get(routes.content.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n if (file.stat.size > MAX_CONTENT_BYTES)\n throw new ArtifactAccessError('This artifact is too large to download.', 413);\n const version = `\"${file.stat.dev}-${file.stat.ino}-${file.stat.size}-${file.stat.mtimeMs}-${file.stat.ctimeMs}\"`;\n const name = encodeURIComponent(path.basename(file.path)).replace(\n /[!'()*]/gu,\n (value) => `%${value.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n const headers = new Headers({\n 'content-type': file.mimeType,\n 'content-disposition': `attachment; filename*=UTF-8''${name}`,\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n 'content-security-policy': \"default-src 'none'; sandbox\",\n 'accept-ranges': 'bytes',\n etag: version,\n });\n const expectedVersion = context.req.header('if-match');\n if (expectedVersion !== undefined && expectedVersion !== version)\n return new Response(null, { status: 412, headers });\n let start = 0;\n let end = file.stat.size - 1;\n const range = context.req.header('range');\n if (range !== undefined) {\n const match = /^bytes=(\\d*)-(\\d*)$/u.exec(range);\n if (\n !match ||\n (!match[1] && !match[2]) ||\n file.stat.size === 0 ||\n [match[1], match[2]].some((value) => value !== '' && !Number.isSafeInteger(Number(value)))\n ) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n if (match[1] === '') {\n const suffix = Number(match[2]);\n start = suffix > 0 ? Math.max(0, file.stat.size - suffix) : file.stat.size;\n } else {\n start = Number(match[1]);\n end = match[2] === '' ? end : Math.min(end, Number(match[2]));\n }\n if (start > end || start >= file.stat.size) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set('content-range', `bytes ${start}-${end}/${file.stat.size}`);\n }\n const bytes = await readArtifactBytes(file.handle, Math.max(0, end - start + 1), start);\n const after = await file.handle.stat();\n if (after.size !== file.stat.size || after.mtimeMs !== file.stat.mtimeMs || after.ctimeMs !== file.stat.ctimeMs)\n return new Response(null, { status: 412, headers });\n headers.set('content-length', String(bytes.byteLength));\n return new Response(bytes, { status: range === undefined ? 200 : 206, headers });\n } finally {\n await file.handle.close();\n }\n });\n return app;\n}\n\nexport const api: DoomApi = {\n basePath: 'artifacts',\n start(context: DoomApiContext) {\n if (!context.sessionId) throw new Error('Artifacts requires a session identity.');\n const app = createArtifactsApi(artifactRoot(context.sessionId, context.environment ?? {}));\n return { fetch: (request) => app.fetch(request), close: () => undefined };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/artifactsApi';\n\nexport default defineRoute(api);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<DoomServerPluginContext, ArtifactServerScope>) => context.root.beforeAgentStart,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<DoomServerPluginContext, ArtifactServerScope>) => context.root.toolExecutionEnd,\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelArtifacts from '../src/extensions/(backend)/channel/artifacts.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server';\nimport hookToolExecutionEnd from '../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-artifact',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: () => ({\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n workspace: () => ({\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n get hooks(): DoomServerSessionPlugin['hooks'] { return [via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'tool_execution_end' }, at(hookToolExecutionEnd, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","names":["rootSession","channelArtifacts","apiRoute","hookBeforeAgentStart","hookToolExecutionEnd"],"sources":["../../src/types/artifacts.ts","../../src/services/artifactSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/artifactChannel/index.ts","../../src/extensions/(backend)/channel/artifacts.server.ts","../../src/types/apiRoutes.ts","../../src/services/artifactsApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts","../../generated/server.ts"],"sourcesContent":["export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import { clearTimeout, setTimeout } from 'node:timers';\n\nimport type { PoHeadlessActivity, PoHeadlessExecutionContext, PoHeadlessHook } from '@agimon-ai/po-core/headless';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { ARTIFACT_REFRESH_MS } from '../../constants/artifacts';\nimport { artifactsChannelType } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, ensureArtifactRoot, listArtifacts } from '../artifactFiles';\n\n/**\n * Creates the session artifact folder. Resolves false when the host has no artifact folder\n * (the edge build in M1 rejects with a 404); Node never rejects with that status here.\n */\nasync function prepareArtifactRoot(root: string): Promise<boolean> {\n try {\n await ensureArtifactRoot(root);\n return true;\n } catch (error) {\n if (error instanceof ArtifactAccessError && error.status === 404) return false;\n throw error;\n }\n}\n\nexport function createArtifactSession({ host }: PoServerPluginContext) {\n const environment = Object.freeze({ ...host.context.environment });\n let active: { context: PoHeadlessExecutionContext; root: string; fingerprint?: string } | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: Promise<void> | undefined;\n let closed = false;\n\n const refresh = (): Promise<void> => {\n if (pending) return pending;\n const runtime = active;\n if (!runtime || closed) return Promise.resolve();\n pending = (async () => {\n const snapshot = await listArtifacts(runtime.root);\n if (closed || active !== runtime) return;\n const fingerprint = JSON.stringify(snapshot);\n if (fingerprint === runtime.fingerprint) return;\n runtime.fingerprint = fingerprint;\n host.context.directEvents?.publish(artifactsChannelType, runtime.context.sessionId, snapshot);\n })().finally(() => {\n pending = undefined;\n });\n return pending;\n };\n const schedule = (): void => {\n if (!active || closed) return;\n timer = setTimeout(() => {\n timer = undefined;\n void refresh().finally(schedule);\n }, ARTIFACT_REFRESH_MS);\n };\n const stop = async (): Promise<void> => {\n active = undefined;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n await pending;\n };\n const activity: PoHeadlessActivity = {\n name: 'artifacts',\n async start(context) {\n if (closed) throw new Error('Artifact session is closed.');\n await stop();\n const root = artifactRoot(context.sessionId, environment);\n if (!(await prepareArtifactRoot(root))) return stop;\n if (closed) throw new Error('Artifact session is closed.');\n active = { context, root };\n await refresh();\n schedule();\n return stop;\n },\n };\n const beforeAgentStart: PoHeadlessHook = {\n event: 'before_agent_start',\n async handle(event, context) {\n const root = artifactRoot(context.sessionId, environment);\n const systemPrompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n if (!(await prepareArtifactRoot(root))) return { systemPrompt };\n const declaration = `Session artifact folder: ${root}`;\n if (systemPrompt.includes(declaration)) return { systemPrompt };\n return {\n systemPrompt:\n `${systemPrompt}\\n\\n${declaration}\\nSave user-designated artifacts and important deliverable documents in this folder, using descriptive filenames. Keep ordinary repository source edits in their existing locations. Do not move existing documents or overwrite artifacts without the user's intent. This folder belongs only to the current session.`.trim(),\n };\n },\n };\n const toolExecutionEnd: PoHeadlessHook = {\n event: 'tool_execution_end',\n handle(_event, context) {\n return active?.context.sessionId === context.sessionId ? refresh() : undefined;\n },\n };\n return {\n activities: [activity],\n beforeAgentStart,\n toolExecutionEnd,\n onDispose: async () => {\n closed = true;\n await stop();\n },\n };\n}\n","import type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createArtifactSession } from '../../../../../services/artifactSession';\n\nexport const createArtifactServerRoot = (context: PoServerPluginContext) => {\n const runtime = createArtifactSession(context);\n return { value: runtime, activities: runtime.activities, onDispose: runtime.onDispose };\n};\nexport type ArtifactServerScope = ReturnType<typeof createArtifactServerRoot>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createArtifactServerRoot);\n","import type { PoHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../types/artifacts';\nimport { artifactRoot, listArtifacts } from '../artifactFiles';\n\nfunction snapshotOf(value: unknown): value is ArtifactSnapshot {\n if (typeof value !== 'object' || value === null) return false;\n const snapshot = value as Partial<ArtifactSnapshot>;\n return (\n Array.isArray(snapshot.items) &&\n (snapshot.error === undefined || typeof snapshot.error === 'string') &&\n snapshot.items.every(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n typeof item.path === 'string' &&\n typeof item.name === 'string' &&\n typeof item.size === 'number' &&\n Number.isFinite(item.size) &&\n typeof item.modifiedAt === 'number' &&\n Number.isFinite(item.modifiedAt),\n )\n );\n}\n\nexport function createArtifactsChannel(): PoHubChannel {\n return {\n frameType: artifactsChannelType,\n start(host) {\n const sessions = new Map<string, { snapshot?: ArtifactSnapshot; unsubscribe: () => void }>();\n return {\n payloadFor(scope) {\n return sessions.get(scope.sessionId)?.snapshot;\n },\n sessionAdded(scope) {\n sessions.get(scope.sessionId)?.unsubscribe();\n const state: { snapshot?: ArtifactSnapshot; unsubscribe: () => void } = { unsubscribe: () => undefined };\n sessions.set(scope.sessionId, state);\n state.unsubscribe = host.directEvents.subscribe(\n artifactsChannelType,\n scope.sessionId,\n (snapshot) => {\n if (!snapshotOf(snapshot) || sessions.get(scope.sessionId) !== state) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n },\n { replayLatest: true },\n );\n void (async () => {\n let snapshot: ArtifactSnapshot;\n try {\n snapshot = await listArtifacts(artifactRoot(scope.sessionId, scope.environment ?? {}));\n } catch (error) {\n snapshot = {\n items: [],\n error: error instanceof Error ? error.message : 'The artifact folder could not be read.',\n };\n }\n // A live session event wins over the earlier seed read.\n if (sessions.get(scope.sessionId) !== state || state.snapshot !== undefined) return;\n state.snapshot = snapshot;\n host.publish(scope.sessionId, snapshot);\n })();\n },\n sessionRemoved(sessionId) {\n sessions.get(sessionId)?.unsubscribe();\n sessions.delete(sessionId);\n },\n close() {\n for (const state of sessions.values()) state.unsubscribe();\n sessions.clear();\n },\n };\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\n\nimport { createArtifactsChannel } from '../../../services/artifactChannel';\n\nexport default defineChannel(createArtifactsChannel);\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import path from 'node:path';\n\nimport type { PoApi, PoApiContext } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { MAX_CONTENT_BYTES, MAX_PREVIEW_BYTES } from '../../constants/artifacts';\nimport routes from '../../types/apiRoutes';\nimport type { ArtifactPreview } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactRoot, listArtifacts, openArtifact, readArtifactBytes } from '../artifactFiles';\n\nexport function createArtifactsApi(root: string): Hono {\n const app = new Hono();\n app.onError((error, context) =>\n context.json(\n { error: error instanceof ArtifactAccessError ? error.message : 'The artifact could not be read.' },\n error instanceof ArtifactAccessError ? error.status : 500,\n ),\n );\n app.get(routes.list.path, async (context) => {\n const snapshot = await listArtifacts(root);\n return context.json(snapshot, snapshot.error === undefined ? 200 : 500);\n });\n app.get(routes.preview.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n const preview: ArtifactPreview = { path: file.path, content: '', mimeType: file.mimeType };\n if (file.stat.size > MAX_PREVIEW_BYTES) {\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n }\n const bytes = await readArtifactBytes(file.handle, MAX_PREVIEW_BYTES + 1);\n if (bytes.byteLength > MAX_PREVIEW_BYTES)\n return context.json({\n ...preview,\n unavailable: true,\n reason: 'This artifact is too large for a text preview.',\n });\n if (bytes.subarray(0, 8192).includes(0))\n return context.json({ ...preview, unavailable: true, reason: 'This artifact is binary.' });\n return context.json({ ...preview, content: new TextDecoder().decode(bytes) });\n } finally {\n await file.handle.close();\n }\n });\n app.get(routes.content.path, async (context) => {\n const file = await openArtifact(root, context.req.query('path'));\n try {\n if (file.stat.size > MAX_CONTENT_BYTES)\n throw new ArtifactAccessError('This artifact is too large to download.', 413);\n const version = `\"${file.stat.dev}-${file.stat.ino}-${file.stat.size}-${file.stat.mtimeMs}-${file.stat.ctimeMs}\"`;\n const name = encodeURIComponent(path.basename(file.path)).replace(\n /[!'()*]/gu,\n (value) => `%${value.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n const headers = new Headers({\n 'content-type': file.mimeType,\n 'content-disposition': `attachment; filename*=UTF-8''${name}`,\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n 'content-security-policy': \"default-src 'none'; sandbox\",\n 'accept-ranges': 'bytes',\n etag: version,\n });\n const expectedVersion = context.req.header('if-match');\n if (expectedVersion !== undefined && expectedVersion !== version)\n return new Response(null, { status: 412, headers });\n let start = 0;\n let end = file.stat.size - 1;\n const range = context.req.header('range');\n if (range !== undefined) {\n const match = /^bytes=(\\d*)-(\\d*)$/u.exec(range);\n if (\n !match ||\n (!match[1] && !match[2]) ||\n file.stat.size === 0 ||\n [match[1], match[2]].some((value) => value !== '' && !Number.isSafeInteger(Number(value)))\n ) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n if (match[1] === '') {\n const suffix = Number(match[2]);\n start = suffix > 0 ? Math.max(0, file.stat.size - suffix) : file.stat.size;\n } else {\n start = Number(match[1]);\n end = match[2] === '' ? end : Math.min(end, Number(match[2]));\n }\n if (start > end || start >= file.stat.size) {\n headers.set('content-range', `bytes */${file.stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set('content-range', `bytes ${start}-${end}/${file.stat.size}`);\n }\n const bytes = await readArtifactBytes(file.handle, Math.max(0, end - start + 1), start);\n const after = await file.handle.stat();\n if (after.size !== file.stat.size || after.mtimeMs !== file.stat.mtimeMs || after.ctimeMs !== file.stat.ctimeMs)\n return new Response(null, { status: 412, headers });\n headers.set('content-length', String(bytes.byteLength));\n return new Response(bytes, { status: range === undefined ? 200 : 206, headers });\n } finally {\n await file.handle.close();\n }\n });\n return app;\n}\n\nexport const api: PoApi = {\n basePath: 'artifacts',\n start(context: PoApiContext) {\n if (!context.sessionId) throw new Error('Artifacts requires a session identity.');\n const app = createArtifactsApi(artifactRoot(context.sessionId, context.environment ?? {}));\n return { fetch: (request) => app.fetch(request), close: () => undefined };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/artifactsApi';\n\nexport default defineRoute(api);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<PoServerPluginContext, ArtifactServerScope>) => context.root.beforeAgentStart,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { ArtifactServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(\n (context: WithRoot<PoServerPluginContext, ArtifactServerScope>) => context.root.toolExecutionEnd,\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelArtifacts from '../src/extensions/(backend)/channel/artifacts.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/artifacts/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server';\nimport hookToolExecutionEnd from '../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-artifact',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: () => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n workspace: () => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'artifacts' }, channelArtifacts())]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return [via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'tool_execution_end' }, at(hookToolExecutionEnd, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
@@ -160,28 +160,28 @@ function ArtifactPreviewView({ path, preview, error, media, loadImage }) {
160
160
  return /* @__PURE__ */ jsxs("div", {
161
161
  "data-testid": "artifact-preview",
162
162
  "data-artifact-path": path,
163
- className: "flex min-h-0 flex-1 flex-col bg-doom-bg text-doom-text",
163
+ className: "flex min-h-0 flex-1 flex-col bg-po-bg text-po-text",
164
164
  children: [/* @__PURE__ */ jsxs("header", {
165
- className: "flex shrink-0 items-center gap-2 border-b border-doom-border px-4 py-3",
165
+ className: "flex shrink-0 items-center gap-2 border-b border-po-border px-4 py-3",
166
166
  children: [/* @__PURE__ */ jsx(Breadcrumb, {
167
167
  path,
168
168
  className: "min-w-0 flex-1"
169
169
  }), /* @__PURE__ */ jsx("span", {
170
- className: "text-sm text-doom-dim",
170
+ className: "text-sm text-po-dim",
171
171
  children: "Artifact"
172
172
  })]
173
173
  }), /* @__PURE__ */ jsx("div", {
174
174
  className: "min-h-0 flex-1 overflow-auto p-4",
175
175
  children: error ? /* @__PURE__ */ jsx("p", {
176
176
  role: "alert",
177
- className: "text-sm text-doom-red",
177
+ className: "text-sm text-po-red",
178
178
  children: error
179
179
  }) : !preview ? /* @__PURE__ */ jsx("p", {
180
180
  role: "status",
181
- className: "text-sm text-doom-dim",
181
+ className: "text-sm text-po-dim",
182
182
  children: "Reading this artifact..."
183
183
  }) : mode === "media" ? media : preview.unavailable ? /* @__PURE__ */ jsx("p", {
184
- className: "text-sm text-doom-dim",
184
+ className: "text-sm text-po-dim",
185
185
  children: preview.reason ?? "This artifact cannot be previewed."
186
186
  }) : mode === "markdown" ? /* @__PURE__ */ jsx(Markdown, {
187
187
  text: preview.content,
@@ -190,14 +190,14 @@ function ArtifactPreviewView({ path, preview, error, media, loadImage }) {
190
190
  title: path,
191
191
  sandbox: "",
192
192
  srcDoc: preview.content,
193
- className: "h-96 w-full rounded-md border border-doom-border bg-doom-deep"
193
+ className: "h-96 w-full rounded-md border border-po-border bg-po-deep"
194
194
  }) : mode === "code" ? /* @__PURE__ */ jsx(CodeEditor, {
195
195
  value: preview.content,
196
196
  path,
197
197
  readOnly: true,
198
- className: "min-h-64 rounded-md border border-doom-border"
198
+ className: "min-h-64 rounded-md border border-po-border"
199
199
  }) : /* @__PURE__ */ jsx("pre", {
200
- className: "whitespace-pre-wrap break-words font-mono text-sm text-doom-text",
200
+ className: "whitespace-pre-wrap break-words font-mono text-sm text-po-text",
201
201
  children: preview.content
202
202
  })
203
203
  })]
@@ -241,7 +241,7 @@ function ArtifactMedia({ sessionId, path, revision }) {
241
241
  contentType: current.mimeType
242
242
  }) : /* @__PURE__ */ jsx("p", {
243
243
  role: current?.error ? "alert" : "status",
244
- className: current?.error ? "text-sm text-doom-red" : "text-sm text-doom-dim",
244
+ className: current?.error ? "text-sm text-po-red" : "text-sm text-po-dim",
245
245
  children: current?.error ?? "Loading artifact media..."
246
246
  });
247
247
  }
@@ -314,16 +314,16 @@ function RecentArtifactsView({ items, loading, error, onOpen }) {
314
314
  children: [
315
315
  error ? /* @__PURE__ */ jsx("p", {
316
316
  role: "alert",
317
- className: "text-sm text-doom-red",
317
+ className: "text-sm text-po-red",
318
318
  children: error
319
319
  }) : null,
320
320
  loading ? /* @__PURE__ */ jsx("p", {
321
321
  role: "status",
322
- className: "text-sm text-doom-dim",
322
+ className: "text-sm text-po-dim",
323
323
  children: "Loading artifacts..."
324
324
  }) : null,
325
325
  !loading && !error && items.length === 0 ? /* @__PURE__ */ jsx("p", {
326
- className: "text-sm text-doom-dim",
326
+ className: "text-sm text-po-dim",
327
327
  children: "No artifacts yet"
328
328
  }) : null,
329
329
  sortedArtifacts(items).slice(0, 3).map((item) => /* @__PURE__ */ jsxs(Button, {
@@ -333,16 +333,16 @@ function RecentArtifactsView({ items, loading, error, onOpen }) {
333
333
  onClick: () => onOpen(item),
334
334
  className: "gap-0.5 px-2 py-1.5",
335
335
  children: [/* @__PURE__ */ jsxs("span", {
336
- className: "flex min-w-0 items-center gap-2 text-base text-doom-text",
336
+ className: "flex min-w-0 items-center gap-2 text-base text-po-text",
337
337
  children: [/* @__PURE__ */ jsx(FileIcon, {
338
338
  "aria-hidden": true,
339
- className: "size-3.5 text-doom-dim"
339
+ className: "size-3.5 text-po-dim"
340
340
  }), /* @__PURE__ */ jsx("span", {
341
341
  className: "truncate",
342
342
  children: item.name
343
343
  })]
344
344
  }), /* @__PURE__ */ jsx("span", {
345
- className: "truncate font-mono text-sm text-doom-dim",
345
+ className: "truncate font-mono text-sm text-po-dim",
346
346
  title: item.path,
347
347
  children: item.path
348
348
  })]
@@ -352,16 +352,16 @@ function RecentArtifactsView({ items, loading, error, onOpen }) {
352
352
  }
353
353
  function ArtifactGridView({ items, loading, error, onOpen }) {
354
354
  return /* @__PURE__ */ jsxs("div", {
355
- className: "flex min-h-0 flex-1 flex-col overflow-auto bg-doom-bg text-doom-text",
355
+ className: "flex min-h-0 flex-1 flex-col overflow-auto bg-po-bg text-po-text",
356
356
  children: [
357
357
  /* @__PURE__ */ jsxs("header", {
358
- className: "flex shrink-0 items-center gap-3 border-b border-doom-border px-4 py-3",
358
+ className: "flex shrink-0 items-center gap-3 border-b border-po-border px-4 py-3",
359
359
  children: [/* @__PURE__ */ jsx("h1", {
360
- className: "text-lg font-semibold text-doom-hi",
360
+ className: "text-lg font-semibold text-po-hi",
361
361
  children: "Artifacts"
362
362
  }), /* @__PURE__ */ jsxs("span", {
363
363
  "data-testid": "artifacts-count",
364
- className: "text-sm text-doom-dim",
364
+ className: "text-sm text-po-dim",
365
365
  children: [
366
366
  items.length,
367
367
  " ",
@@ -371,12 +371,12 @@ function ArtifactGridView({ items, loading, error, onOpen }) {
371
371
  }),
372
372
  error ? /* @__PURE__ */ jsx("p", {
373
373
  role: "alert",
374
- className: "px-4 py-3 text-sm text-doom-red",
374
+ className: "px-4 py-3 text-sm text-po-red",
375
375
  children: error
376
376
  }) : null,
377
377
  loading ? /* @__PURE__ */ jsx("p", {
378
378
  role: "status",
379
- className: "px-4 py-3 text-sm text-doom-dim",
379
+ className: "px-4 py-3 text-sm text-po-dim",
380
380
  children: "Loading artifacts..."
381
381
  }) : null,
382
382
  !loading && !error && items.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
@@ -396,20 +396,20 @@ function ArtifactGridView({ items, loading, error, onOpen }) {
396
396
  children: [
397
397
  /* @__PURE__ */ jsx(FileIcon, {
398
398
  "aria-hidden": true,
399
- className: "size-5 text-doom-green"
399
+ className: "size-5 text-po-green"
400
400
  }),
401
401
  /* @__PURE__ */ jsx("span", {
402
- className: "truncate text-base font-semibold text-doom-hi",
402
+ className: "truncate text-base font-semibold text-po-hi",
403
403
  title: item.name,
404
404
  children: item.name
405
405
  }),
406
406
  /* @__PURE__ */ jsx("span", {
407
- className: "truncate font-mono text-sm text-doom-dim",
407
+ className: "truncate font-mono text-sm text-po-dim",
408
408
  title: item.path,
409
409
  children: item.path
410
410
  }),
411
411
  /* @__PURE__ */ jsx("span", {
412
- className: "text-sm text-doom-dim",
412
+ className: "text-sm text-po-dim",
413
413
  children: sizeLabel(item.size)
414
414
  })
415
415
  ]
@@ -466,7 +466,7 @@ function ArtifactsPanel({ sessionId, openTransientTab }) {
466
466
  var artifacts_web_default$1 = defineActivityGroup({
467
467
  icon: FileIcon,
468
468
  keys: "",
469
- statusKey: "doom-artifacts",
469
+ statusKey: "po-artifacts",
470
470
  activeSource: artifactActivitySource,
471
471
  transientTab: artifactsTab,
472
472
  marksBackgroundWork: false,
@@ -1 +1 @@
1
- {"version":3,"file":"web.mjs","names":["activityGroupArtifacts","channelArtifacts","fillArtifacts"],"sources":["../../src/types/apiRoutes.ts","../../generated/client.ts","../../src/types/artifacts.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactView.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactsStore.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactsApi.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewView.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsView.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web.ts","../../src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.web.tsx","../../generated/web.ts"],"sourcesContent":["import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\nimport { sealedTransport } from '@agimon-ai/po-web-security/browser';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'artifacts' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'artifacts',\n transport: (input, init) => sealedTransport.fetch(input, init),\n sessionAddress: sessionApiAddress,\n});\n","export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import type { ArtifactItem, ArtifactSnapshot } from '../../../../../types/artifacts';\n\nexport function sortedArtifacts(items: readonly ArtifactItem[]): ArtifactItem[] {\n return [...items].sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n}\n\nexport function artifactTabId(sessionId: string, path: string): string {\n return `artifact:${encodeURIComponent(sessionId)}:${encodeURIComponent(path)}`;\n}\n\nexport function artifactsTabId(sessionId: string): string {\n return `artifacts-${encodeURIComponent(sessionId)}`;\n}\n\n/** Resolves document assets within the artifact folder, never into another filesystem root. */\nexport function artifactAssetPath(documentPath: string, imagePath: string): string {\n if (!imagePath || imagePath.startsWith('/') || imagePath.includes('\\\\') || /^[a-z][a-z0-9+.-]*:/iu.test(imagePath))\n throw new Error('Artifact images must use a relative path.');\n const segments = documentPath.split('/').slice(0, -1);\n for (const segment of imagePath.split('/')) {\n if (segment === '' || segment === '.') continue;\n if (segment === '..') {\n if (segments.length === 0) throw new Error('The image is outside this artifact folder.');\n segments.pop();\n } else segments.push(segment);\n }\n if (segments.length === 0) throw new Error('The image path is empty.');\n return segments.join('/');\n}\n\nexport function artifactPreviewMode(path: string): 'markdown' | 'html' | 'media' | 'code' | 'text' {\n if (/\\.(?:png|jpe?g|gif|webp|avif|bmp|svg|ico|mp3|wav|m4a|ogg|mp4|mov|webm|pdf)$/iu.test(path)) return 'media';\n if (/\\.(?:md|markdown)$/iu.test(path)) return 'markdown';\n if (/\\.html?$/iu.test(path)) return 'html';\n if (/\\.(?:[cm]?[jt]sx?|json|ya?ml|css|scss|py|sh|bash|go|rs|rb|java|c|cpp|h|sql|xml|toml)$/iu.test(path))\n return 'code';\n return 'text';\n}\n\n/** A malformed channel frame must not replace the current session's known inventory. */\nexport function parseArtifactSnapshot(input: unknown): ArtifactSnapshot | null {\n if (typeof input !== 'object' || input === null || !('items' in input) || !Array.isArray(input.items)) return null;\n if ('error' in input && input.error !== undefined && typeof input.error !== 'string') return null;\n const items: ArtifactItem[] = [];\n for (const item of input.items) {\n if (\n typeof item !== 'object' ||\n item === null ||\n typeof item.path !== 'string' ||\n !item.path ||\n item.path.startsWith('/') ||\n item.path.includes('\\\\') ||\n item.path.split('/').some((part: string) => part === '..' || part === '.') ||\n typeof item.name !== 'string' ||\n !item.name ||\n typeof item.size !== 'number' ||\n !Number.isFinite(item.size) ||\n item.size < 0 ||\n typeof item.modifiedAt !== 'number' ||\n !Number.isFinite(item.modifiedAt) ||\n item.modifiedAt < 0\n )\n return null;\n items.push({ path: item.path, name: item.name, size: item.size, modifiedAt: item.modifiedAt });\n }\n return {\n items: sortedArtifacts(items),\n ...('error' in input && typeof input.error === 'string' ? { error: input.error } : {}),\n };\n}\n","import { defineSessionStore } from '@agimon-ai/po-core/web';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../../../../types/artifacts';\nimport { parseArtifactSnapshot } from './artifactView';\n\nexport const artifacts = defineSessionStore<ArtifactSnapshot & { loaded: boolean }>({ items: [], loaded: false });\n\nexport const artifactsChannel = artifacts.channel<ArtifactSnapshot>({\n channel: artifactsChannelType,\n parse: parseArtifactSnapshot,\n reduce: (_current, snapshot) => ({ ...snapshot, loaded: true }),\n});\n\n/** Artifact documents are outcomes, not background work. The source also keeps the empty launcher visible. */\nexport const artifactActivitySource = {\n subscribe(listener: () => void) {\n const subscription = artifacts.store.subscribe(listener);\n return () => subscription.unsubscribe();\n },\n isActive: () => false,\n};\n","import { api } from '../../../../../../generated/client';\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifacts } from './artifactsStore';\nimport { artifactAssetPath, parseArtifactSnapshot } from './artifactView';\n\nexport async function refreshArtifacts(sessionId: string, signal: AbortSignal): Promise<void> {\n const before = artifacts.select(artifacts.store.state, sessionId);\n try {\n const result = await api.session(sessionId).list({ signal });\n if (signal.aborted || artifacts.select(artifacts.store.state, sessionId) !== before) return;\n const snapshot = result.ok ? parseArtifactSnapshot(result.data) : null;\n artifacts.update(sessionId, () =>\n snapshot\n ? { ...snapshot, loaded: true }\n : {\n ...before,\n loaded: true,\n error: result.ok\n ? 'The session answered with no artifact list.'\n : result.error || 'Artifacts could not be loaded.',\n },\n );\n } catch (error) {\n if (!signal.aborted && artifacts.select(artifacts.store.state, sessionId) === before)\n artifacts.update(sessionId, () => ({\n ...before,\n loaded: true,\n error: error instanceof Error ? error.message : String(error),\n }));\n }\n}\n\nexport async function fetchArtifactPreview(\n sessionId: string,\n path: string,\n signal: AbortSignal,\n): Promise<ArtifactPreview> {\n const result = await api.session(sessionId).preview({ query: { path }, signal });\n if (!result.ok) throw new Error(result.error || 'This artifact could not be read.');\n const preview = result.data;\n if (\n !preview ||\n typeof preview.path !== 'string' ||\n typeof preview.content !== 'string' ||\n typeof preview.mimeType !== 'string'\n )\n throw new Error('The session answered with no artifact.');\n return preview;\n}\n\n/** Generated raw calls keep media and Markdown asset reads on the sealed artifact transport. */\nexport async function artifactBlob(sessionId: string, path: string, signal?: AbortSignal): Promise<Blob> {\n const result = await api.session(sessionId).content({ query: { path }, ...(signal ? { signal } : {}) });\n if (!result.ok) throw new Error(result.error || 'This artifact could not be loaded.');\n return result.response.blob();\n}\n\nexport async function loadArtifactImage(sessionId: string, documentPath: string, imagePath: string) {\n const blob = await artifactBlob(sessionId, artifactAssetPath(documentPath, imagePath));\n if (!blob.type.startsWith('image/')) throw new Error('This artifact asset is not an image.');\n const url = URL.createObjectURL(blob);\n return { url, dispose: () => URL.revokeObjectURL(url) };\n}\n","import { Breadcrumb, CodeEditor, Markdown } from '@agimon-ai/po-web-components';\nimport type { ReactNode } from 'react';\n\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifactPreviewMode } from '../_lib/artifactView';\n\ninterface ArtifactPreviewViewProps {\n path: string;\n preview?: ArtifactPreview;\n error?: string;\n media?: ReactNode;\n loadImage?: (path: string) => Promise<{ url: string; dispose: () => void }>;\n}\n\nexport function ArtifactPreviewView({ path, preview, error, media, loadImage }: ArtifactPreviewViewProps) {\n const mode = artifactPreviewMode(path);\n return (\n <div\n data-testid=\"artifact-preview\"\n data-artifact-path={path}\n className=\"flex min-h-0 flex-1 flex-col bg-doom-bg text-doom-text\"\n >\n <header className=\"flex shrink-0 items-center gap-2 border-b border-doom-border px-4 py-3\">\n <Breadcrumb path={path} className=\"min-w-0 flex-1\" />\n <span className=\"text-sm text-doom-dim\">Artifact</span>\n </header>\n <div className=\"min-h-0 flex-1 overflow-auto p-4\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-doom-red\">\n {error}\n </p>\n ) : !preview ? (\n <p role=\"status\" className=\"text-sm text-doom-dim\">\n Reading this artifact...\n </p>\n ) : mode === 'media' ? (\n media\n ) : preview.unavailable ? (\n <p className=\"text-sm text-doom-dim\">{preview.reason ?? 'This artifact cannot be previewed.'}</p>\n ) : mode === 'markdown' ? (\n <Markdown text={preview.content} loadImage={loadImage} />\n ) : mode === 'html' ? (\n <iframe\n title={path}\n sandbox=\"\"\n srcDoc={preview.content}\n className=\"h-96 w-full rounded-md border border-doom-border bg-doom-deep\"\n />\n ) : mode === 'code' ? (\n <CodeEditor\n value={preview.content}\n path={path}\n readOnly\n className=\"min-h-64 rounded-md border border-doom-border\"\n />\n ) : (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-sm text-doom-text\">{preview.content}</pre>\n )}\n </div>\n </div>\n );\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { MediaPreview } from '@agimon-ai/po-web-components';\nimport { useStore } from '@tanstack/react-store';\nimport { useCallback, useEffect, useState } from 'react';\n\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifactBlob, fetchArtifactPreview, loadArtifactImage } from '../_lib/artifactsApi';\nimport { artifacts } from '../_lib/artifactsStore';\nimport { artifactTabId } from '../_lib/artifactView';\nimport { ArtifactPreviewView } from './ArtifactPreviewView';\n\nfunction ArtifactMedia({ sessionId, path, revision }: { sessionId: string; path: string; revision: string }) {\n const [loaded, setLoaded] = useState<{ source: string; url?: string; mimeType?: string; error?: string }>();\n const source = `${sessionId}:${path}:${revision}`;\n useEffect(() => {\n const controller = new AbortController();\n let url: string | undefined;\n void artifactBlob(sessionId, path, controller.signal)\n .then((blob) => {\n if (controller.signal.aborted) return;\n url = URL.createObjectURL(blob);\n setLoaded({ source, url, mimeType: blob.type });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setLoaded({ source, error: error instanceof Error ? error.message : String(error) });\n });\n return () => {\n controller.abort();\n if (url !== undefined) URL.revokeObjectURL(url);\n };\n }, [sessionId, path, source]);\n const current = loaded?.source === source ? loaded : undefined;\n return current?.url ? (\n <MediaPreview src={current.url} path={path} contentType={current.mimeType} />\n ) : (\n <p\n role={current?.error ? 'alert' : 'status'}\n className={current?.error ? 'text-sm text-doom-red' : 'text-sm text-doom-dim'}\n >\n {current?.error ?? 'Loading artifact media...'}\n </p>\n );\n}\n\nexport function ArtifactPreviewPanel({ path, sessionId }: WebPluginSlotProps & { path: string }) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n const item = snapshot.items.find((candidate) => candidate.path === path);\n const revision = item ? `${item.modifiedAt}:${item.size}` : snapshot.loaded ? 'missing' : 'initial';\n const [loaded, setLoaded] = useState<{\n sessionId: string;\n path: string;\n revision: string;\n preview?: ArtifactPreview;\n error?: string;\n }>();\n useEffect(() => {\n if (sessionId === null || revision === 'missing') return;\n const controller = new AbortController();\n void fetchArtifactPreview(sessionId, path, controller.signal)\n .then((preview) => {\n if (!controller.signal.aborted) setLoaded({ sessionId, path, revision, preview });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setLoaded({ sessionId, path, revision, error: error instanceof Error ? error.message : String(error) });\n });\n return () => controller.abort();\n }, [sessionId, path, revision]);\n const current =\n loaded?.sessionId === sessionId && loaded.path === path && loaded.revision === revision ? loaded : undefined;\n const loadImage = useCallback(\n (imagePath: string) => {\n if (sessionId === null) return Promise.reject(new Error('The session is unavailable.'));\n return loadArtifactImage(sessionId, path, imagePath);\n },\n [sessionId, path],\n );\n return (\n <ArtifactPreviewView\n path={path}\n preview={current?.preview}\n error={\n sessionId === null\n ? 'The session is unavailable.'\n : revision === 'missing'\n ? 'This artifact no longer exists.'\n : current?.error\n }\n loadImage={loadImage}\n media={sessionId === null ? undefined : <ArtifactMedia sessionId={sessionId} path={path} revision={revision} />}\n />\n );\n}\n\nexport function artifactTab(sessionId: string, path: string): TransientTab {\n return {\n id: artifactTabId(sessionId, path),\n label: path.split('/').at(-1) ?? path,\n panel: (props) => <ArtifactPreviewPanel {...props} sessionId={sessionId} path={path} />,\n };\n}\n","import { Button, EmptyState, FileIcon } from '@agimon-ai/po-web-components';\n\nimport type { ArtifactItem } from '../../../../../types/artifacts';\nimport { sortedArtifacts } from '../_lib/artifactView';\n\ninterface ArtifactsViewProps {\n items: readonly ArtifactItem[];\n loading?: boolean;\n error?: string;\n onOpen: (item: ArtifactItem) => void;\n}\n\nfunction sizeLabel(size: number): string {\n return size < 1024\n ? `${size} B`\n : size < 1024 * 1024\n ? `${Math.ceil(size / 1024)} KB`\n : `${(size / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport function RecentArtifactsView({ items, loading, error, onOpen }: ArtifactsViewProps) {\n return (\n <div data-testid=\"artifacts-recent-items\" className=\"flex flex-col gap-1\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-doom-red\">\n {error}\n </p>\n ) : null}\n {loading ? (\n <p role=\"status\" className=\"text-sm text-doom-dim\">\n Loading artifacts...\n </p>\n ) : null}\n {!loading && !error && items.length === 0 ? <p className=\"text-sm text-doom-dim\">No artifacts yet</p> : null}\n {sortedArtifacts(items)\n .slice(0, 3)\n .map((item) => (\n <Button\n key={item.path}\n variant=\"ghost\"\n size=\"card\"\n data-artifact-path={item.path}\n onClick={() => onOpen(item)}\n className=\"gap-0.5 px-2 py-1.5\"\n >\n <span className=\"flex min-w-0 items-center gap-2 text-base text-doom-text\">\n <FileIcon aria-hidden className=\"size-3.5 text-doom-dim\" />\n <span className=\"truncate\">{item.name}</span>\n </span>\n <span className=\"truncate font-mono text-sm text-doom-dim\" title={item.path}>\n {item.path}\n </span>\n </Button>\n ))}\n </div>\n );\n}\n\nexport function ArtifactGridView({ items, loading, error, onOpen }: ArtifactsViewProps) {\n return (\n <div className=\"flex min-h-0 flex-1 flex-col overflow-auto bg-doom-bg text-doom-text\">\n <header className=\"flex shrink-0 items-center gap-3 border-b border-doom-border px-4 py-3\">\n <h1 className=\"text-lg font-semibold text-doom-hi\">Artifacts</h1>\n <span data-testid=\"artifacts-count\" className=\"text-sm text-doom-dim\">\n {items.length} {items.length === 1 ? 'artifact' : 'artifacts'}\n </span>\n </header>\n {error ? (\n <p role=\"alert\" className=\"px-4 py-3 text-sm text-doom-red\">\n {error}\n </p>\n ) : null}\n {loading ? (\n <p role=\"status\" className=\"px-4 py-3 text-sm text-doom-dim\">\n Loading artifacts...\n </p>\n ) : null}\n {!loading && !error && items.length === 0 ? (\n <EmptyState\n title=\"No artifacts yet\"\n description=\"Documents Po prepares for this session will appear here.\"\n className=\"px-4 py-8\"\n />\n ) : null}\n <div data-testid=\"artifacts-grid\" className=\"grid grid-cols-1 gap-3 p-4 sm:grid-cols-2 xl:grid-cols-3\">\n {sortedArtifacts(items).map((item) => (\n <Button\n key={item.path}\n variant=\"outline\"\n size=\"card\"\n data-artifact-path={item.path}\n onClick={() => onOpen(item)}\n className=\"min-w-0 gap-2 p-4\"\n >\n <FileIcon aria-hidden className=\"size-5 text-doom-green\" />\n <span className=\"truncate text-base font-semibold text-doom-hi\" title={item.name}>\n {item.name}\n </span>\n <span className=\"truncate font-mono text-sm text-doom-dim\" title={item.path}>\n {item.path}\n </span>\n <span className=\"text-sm text-doom-dim\">{sizeLabel(item.size)}</span>\n </Button>\n ))}\n </div>\n </div>\n );\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { useStore } from '@tanstack/react-store';\nimport { useEffect } from 'react';\n\nimport { refreshArtifacts } from '../_lib/artifactsApi';\nimport { artifacts } from '../_lib/artifactsStore';\nimport { artifactTab } from './ArtifactPreviewPanel';\nimport { ArtifactGridView, RecentArtifactsView } from './ArtifactsView';\n\nexport function artifactsTab(): TransientTab {\n // The host stores transient tabs per session, including this heading factory's fixed id.\n return { id: 'artifacts-grid', label: 'Artifacts', panel: ArtifactsPanel };\n}\n\nexport function ArtifactsActivitySection({ sessionId, openTransientTab }: WebPluginSlotProps) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n useEffect(() => {\n if (sessionId === null || snapshot.loaded) return;\n const controller = new AbortController();\n void refreshArtifacts(sessionId, controller.signal);\n return () => controller.abort();\n }, [sessionId, snapshot.loaded]);\n return (\n <RecentArtifactsView\n items={snapshot.items}\n loading={!snapshot.loaded}\n error={snapshot.error}\n onOpen={(item) => {\n if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));\n }}\n />\n );\n}\n\nexport function ArtifactsPanel({ sessionId, openTransientTab }: WebPluginSlotProps) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n useEffect(() => {\n if (sessionId === null) return;\n const controller = new AbortController();\n void refreshArtifacts(sessionId, controller.signal);\n return () => controller.abort();\n }, [sessionId]);\n return (\n <ArtifactGridView\n items={snapshot.items}\n loading={!snapshot.loaded && sessionId !== null}\n error={sessionId === null ? 'The session is unavailable.' : snapshot.error}\n onOpen={(item) => {\n if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));\n }}\n />\n );\n}\n","import { defineActivityGroup } from '@agimon-ai/po-core/web';\nimport { FileIcon } from '@agimon-ai/po-web-components';\n\nimport { artifactsTab } from '../_components/ArtifactsPanel';\nimport { artifactActivitySource } from '../_lib/artifactsStore';\nexport default defineActivityGroup({\n icon: FileIcon,\n keys: '',\n statusKey: 'doom-artifacts',\n activeSource: artifactActivitySource,\n transientTab: artifactsTab,\n marksBackgroundWork: false,\n order: 15,\n});\n","import { defineChannelFile } from '@agimon-ai/po-core/web';\n\nimport { artifactsChannel } from '../_lib/artifactsStore';\nexport default defineChannelFile(artifactsChannel);\n","import { defineFill } from '@agimon-ai/po-core/web';\n\nimport { ArtifactsActivitySection } from '../_components/ArtifactsPanel';\nexport default defineFill({ component: ArtifactsActivitySection });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/po-core/web';\n\nimport activityGroupArtifacts from '../src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web';\nimport channelArtifacts from '../src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web';\nimport fillArtifacts from '../src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'artifacts',\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'artifacts' }, activityGroupArtifacts)]; },\n get channels(): WebPluginContributions['channels'] { return [via({ channel: 'artifacts' }, channelArtifacts)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'activity.artifacts', id: 'artifacts' }, fillArtifacts)]; },\n },\n});\n"],"mappings":";;;;;;ACSA,MAAa,MAAM,gBAAgB;CDJjC,MAAM;EACJ,QAAQ;EACR,MAAM;EACN,UAAU,KAAA;CACZ;CACA,SAAS;EACP,QAAQ;EACR,MAAM;EACN,OAAO,CAAC,MAAM;EACd,UAAU,KAAA;CACZ;CACA,SAAS;EACP,QAAQ;EACR,MAAM;EACN,OAAO,CAAC,MAAM;EACd,KAAK;CACP;ACZiC,GAAQ;CACzC,QAAQ,CAHgB,SAGhB;CACR,UAAU;CACV,YAAY,OAAO,SAAS,gBAAgB,MAAM,OAAO,IAAI;CAC7D,gBAAgB;AAClB,CAAC;;;ACOD,MAAa,uBAAuB;;;ACnBpC,SAAgB,gBAAgB,OAAgD;CAC9E,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,MACf,MAAM,UACL,MAAM,aAAa,KAAK,eAAe,KAAK,OAAO,MAAM,OAAO,KAAK,KAAK,OAAO,MAAM,OAAO,IAAI,EACtG;AACF;AAEA,SAAgB,cAAc,WAAmB,MAAsB;CACrE,OAAO,YAAY,mBAAmB,SAAS,EAAE,GAAG,mBAAmB,IAAI;AAC7E;;AAOA,SAAgB,kBAAkB,cAAsB,WAA2B;CACjF,IAAI,CAAC,aAAa,UAAU,WAAW,GAAG,KAAK,UAAU,SAAS,IAAI,KAAK,wBAAwB,KAAK,SAAS,GAC/G,MAAM,IAAI,MAAM,2CAA2C;CAC7D,MAAM,WAAW,aAAa,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,EAAE;CACpD,KAAK,MAAM,WAAW,UAAU,MAAM,GAAG,GAAG;EAC1C,IAAI,YAAY,MAAM,YAAY,KAAK;EACvC,IAAI,YAAY,MAAM;GACpB,IAAI,SAAS,WAAW,GAAG,MAAM,IAAI,MAAM,4CAA4C;GACvF,SAAS,IAAI;EACf,OAAO,SAAS,KAAK,OAAO;CAC9B;CACA,IAAI,SAAS,WAAW,GAAG,MAAM,IAAI,MAAM,0BAA0B;CACrE,OAAO,SAAS,KAAK,GAAG;AAC1B;AAEA,SAAgB,oBAAoB,MAA+D;CACjG,IAAI,gFAAgF,KAAK,IAAI,GAAG,OAAO;CACvG,IAAI,uBAAuB,KAAK,IAAI,GAAG,OAAO;CAC9C,IAAI,aAAa,KAAK,IAAI,GAAG,OAAO;CACpC,IAAI,0FAA0F,KAAK,IAAI,GACrG,OAAO;CACT,OAAO;AACT;;AAGA,SAAgB,sBAAsB,OAAyC;CAC7E,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,WAAW,UAAU,CAAC,MAAM,QAAQ,MAAM,KAAK,GAAG,OAAO;CAC9G,IAAI,WAAW,SAAS,MAAM,UAAU,KAAA,KAAa,OAAO,MAAM,UAAU,UAAU,OAAO;CAC7F,MAAM,QAAwB,CAAC;CAC/B,KAAK,MAAM,QAAQ,MAAM,OAAO;EAC9B,IACE,OAAO,SAAS,YAChB,SAAS,QACT,OAAO,KAAK,SAAS,YACrB,CAAC,KAAK,QACN,KAAK,KAAK,WAAW,GAAG,KACxB,KAAK,KAAK,SAAS,IAAI,KACvB,KAAK,KAAK,MAAM,GAAG,CAAC,CAAC,MAAM,SAAiB,SAAS,QAAQ,SAAS,GAAG,KACzE,OAAO,KAAK,SAAS,YACrB,CAAC,KAAK,QACN,OAAO,KAAK,SAAS,YACrB,CAAC,OAAO,SAAS,KAAK,IAAI,KAC1B,KAAK,OAAO,KACZ,OAAO,KAAK,eAAe,YAC3B,CAAC,OAAO,SAAS,KAAK,UAAU,KAChC,KAAK,aAAa,GAElB,OAAO;EACT,MAAM,KAAK;GAAE,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM,YAAY,KAAK;EAAW,CAAC;CAC/F;CACA,OAAO;EACL,OAAO,gBAAgB,KAAK;EAC5B,GAAI,WAAW,SAAS,OAAO,MAAM,UAAU,WAAW,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;CACtF;AACF;;;ACnEA,MAAa,YAAY,mBAA2D;CAAE,OAAO,CAAC;CAAG,QAAQ;AAAM,CAAC;AAEhH,MAAa,mBAAmB,UAAU,QAA0B;CAClE,SAAS;CACT,OAAO;CACP,SAAS,UAAU,cAAc;EAAE,GAAG;EAAU,QAAQ;CAAK;AAC/D,CAAC;;AAGD,MAAa,yBAAyB;CACpC,UAAU,UAAsB;EAC9B,MAAM,eAAe,UAAU,MAAM,UAAU,QAAQ;EACvD,aAAa,aAAa,YAAY;CACxC;CACA,gBAAgB;AAClB;;;ACfA,eAAsB,iBAAiB,WAAmB,QAAoC;CAC5F,MAAM,SAAS,UAAU,OAAO,UAAU,MAAM,OAAO,SAAS;CAChE,IAAI;EACF,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC;EAC3D,IAAI,OAAO,WAAW,UAAU,OAAO,UAAU,MAAM,OAAO,SAAS,MAAM,QAAQ;EACrF,MAAM,WAAW,OAAO,KAAK,sBAAsB,OAAO,IAAI,IAAI;EAClE,UAAU,OAAO,iBACf,WACI;GAAE,GAAG;GAAU,QAAQ;EAAK,IAC5B;GACE,GAAG;GACH,QAAQ;GACR,OAAO,OAAO,KACV,gDACA,OAAO,SAAS;EACtB,CACN;CACF,SAAS,OAAO;EACd,IAAI,CAAC,OAAO,WAAW,UAAU,OAAO,UAAU,MAAM,OAAO,SAAS,MAAM,QAC5E,UAAU,OAAO,kBAAkB;GACjC,GAAG;GACH,QAAQ;GACR,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAC9D,EAAE;CACN;AACF;AAEA,eAAsB,qBACpB,WACA,MACA,QAC0B;CAC1B,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS,CAAC,CAAC,QAAQ;EAAE,OAAO,EAAE,KAAK;EAAG;CAAO,CAAC;CAC/E,IAAI,CAAC,OAAO,IAAI,MAAM,IAAI,MAAM,OAAO,SAAS,kCAAkC;CAClF,MAAM,UAAU,OAAO;CACvB,IACE,CAAC,WACD,OAAO,QAAQ,SAAS,YACxB,OAAO,QAAQ,YAAY,YAC3B,OAAO,QAAQ,aAAa,UAE5B,MAAM,IAAI,MAAM,wCAAwC;CAC1D,OAAO;AACT;;AAGA,eAAsB,aAAa,WAAmB,MAAc,QAAqC;CACvG,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS,CAAC,CAAC,QAAQ;EAAE,OAAO,EAAE,KAAK;EAAG,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;CAAG,CAAC;CACtG,IAAI,CAAC,OAAO,IAAI,MAAM,IAAI,MAAM,OAAO,SAAS,oCAAoC;CACpF,OAAO,OAAO,SAAS,KAAK;AAC9B;AAEA,eAAsB,kBAAkB,WAAmB,cAAsB,WAAmB;CAClG,MAAM,OAAO,MAAM,aAAa,WAAW,kBAAkB,cAAc,SAAS,CAAC;CACrF,IAAI,CAAC,KAAK,KAAK,WAAW,QAAQ,GAAG,MAAM,IAAI,MAAM,sCAAsC;CAC3F,MAAM,MAAM,IAAI,gBAAgB,IAAI;CACpC,OAAO;EAAE;EAAK,eAAe,IAAI,gBAAgB,GAAG;CAAE;AACxD;;;AChDA,SAAgB,oBAAoB,EAAE,MAAM,SAAS,OAAO,OAAO,aAAuC;CACxG,MAAM,OAAO,oBAAoB,IAAI;CACrC,OACE,qBAAC,OAAD;EACE,eAAY;EACZ,sBAAoB;EACpB,WAAU;EAHZ,UAAA,CAKE,qBAAC,UAAD;GAAQ,WAAU;GAAlB,UAAA,CACE,oBAAC,YAAD;IAAkB;IAAM,WAAU;GAAkB,CAAA,GACpD,oBAAC,QAAD;IAAM,WAAU;IAAwB,UAAA;GAAc,CAAA,CAChD;EACR,CAAA,GAAA,oBAAC,OAAD;GAAK,WAAU;GACZ,UAAA,QACC,oBAAC,KAAD;IAAG,MAAK;IAAQ,WAAU;IACvB,UAAA;GACA,CAAA,IACD,CAAC,UACH,oBAAC,KAAD;IAAG,MAAK;IAAS,WAAU;IAAwB,UAAA;GAEhD,CAAA,IACD,SAAS,UACX,QACE,QAAQ,cACV,oBAAC,KAAD;IAAG,WAAU;IAAyB,UAAA,QAAQ,UAAU;GAAwC,CAAA,IAC9F,SAAS,aACX,oBAAC,UAAD;IAAU,MAAM,QAAQ;IAAoB;GAAY,CAAA,IACtD,SAAS,SACX,oBAAC,UAAD;IACE,OAAO;IACP,SAAQ;IACR,QAAQ,QAAQ;IAChB,WAAU;GACX,CAAA,IACC,SAAS,SACX,oBAAC,YAAD;IACE,OAAO,QAAQ;IACT;IACN,UAAA;IACA,WAAU;GACX,CAAA,IAED,oBAAC,OAAD;IAAK,WAAU;IAAoE,UAAA,QAAQ;GAAa,CAAA;EAEvG,CAAA,CACF;;AAET;;;AClDA,SAAS,cAAc,EAAE,WAAW,MAAM,YAAmE;CAC3G,MAAM,CAAC,QAAQ,aAAa,SAA8E;CAC1G,MAAM,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG;CACvC,gBAAgB;EACd,MAAM,aAAa,IAAI,gBAAgB;EACvC,IAAI;EACJ,aAAkB,WAAW,MAAM,WAAW,MAAM,CAAC,CAClD,MAAM,SAAS;GACd,IAAI,WAAW,OAAO,SAAS;GAC/B,MAAM,IAAI,gBAAgB,IAAI;GAC9B,UAAU;IAAE;IAAQ;IAAK,UAAU,KAAK;GAAK,CAAC;EAChD,CAAC,CAAC,CACD,OAAO,UAAmB;GACzB,IAAI,CAAC,WAAW,OAAO,SACrB,UAAU;IAAE;IAAQ,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAAE,CAAC;EACvF,CAAC;EACH,aAAa;GACX,WAAW,MAAM;GACjB,IAAI,QAAQ,KAAA,GAAW,IAAI,gBAAgB,GAAG;EAChD;CACF,GAAG;EAAC;EAAW;EAAM;CAAM,CAAC;CAC5B,MAAM,UAAU,QAAQ,WAAW,SAAS,SAAS,KAAA;CACrD,OAAO,SAAS,MACd,oBAAC,cAAD;EAAc,KAAK,QAAQ;EAAW;EAAM,aAAa,QAAQ;CAAW,CAAA,IAE5E,oBAAC,KAAD;EACE,MAAM,SAAS,QAAQ,UAAU;EACjC,WAAW,SAAS,QAAQ,0BAA0B;EAErD,UAAA,SAAS,SAAS;CAClB,CAAA;AAEP;AAEA,SAAgB,qBAAqB,EAAE,MAAM,aAAoD;CAC/F,MAAM,WAAW,SAAS,UAAU,QAAQ,UAAU,UAAU,OAAO,OAAO,SAAS,CAAC;CACxF,MAAM,OAAO,SAAS,MAAM,MAAM,cAAc,UAAU,SAAS,IAAI;CACvE,MAAM,WAAW,OAAO,GAAG,KAAK,WAAW,GAAG,KAAK,SAAS,SAAS,SAAS,YAAY;CAC1F,MAAM,CAAC,QAAQ,aAAa,SAMzB;CACH,gBAAgB;EACd,IAAI,cAAc,QAAQ,aAAa,WAAW;EAClD,MAAM,aAAa,IAAI,gBAAgB;EACvC,qBAA0B,WAAW,MAAM,WAAW,MAAM,CAAC,CAC1D,MAAM,YAAY;GACjB,IAAI,CAAC,WAAW,OAAO,SAAS,UAAU;IAAE;IAAW;IAAM;IAAU;GAAQ,CAAC;EAClF,CAAC,CAAC,CACD,OAAO,UAAmB;GACzB,IAAI,CAAC,WAAW,OAAO,SACrB,UAAU;IAAE;IAAW;IAAM;IAAU,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAAE,CAAC;EAC1G,CAAC;EACH,aAAa,WAAW,MAAM;CAChC,GAAG;EAAC;EAAW;EAAM;CAAQ,CAAC;CAC9B,MAAM,UACJ,QAAQ,cAAc,aAAa,OAAO,SAAS,QAAQ,OAAO,aAAa,WAAW,SAAS,KAAA;CACrG,MAAM,YAAY,aACf,cAAsB;EACrB,IAAI,cAAc,MAAM,OAAO,QAAQ,uBAAO,IAAI,MAAM,6BAA6B,CAAC;EACtF,OAAO,kBAAkB,WAAW,MAAM,SAAS;CACrD,GACA,CAAC,WAAW,IAAI,CAClB;CACA,OACE,oBAAC,qBAAD;EACQ;EACN,SAAS,SAAS;EAClB,OACE,cAAc,OACV,gCACA,aAAa,YACX,oCACA,SAAS;EAEN;EACX,OAAO,cAAc,OAAO,KAAA,IAAY,oBAAC,eAAD;GAA0B;GAAiB;GAAgB;EAAW,CAAA;CAC/G,CAAA;AAEL;AAEA,SAAgB,YAAY,WAAmB,MAA4B;CACzE,OAAO;EACL,IAAI,cAAc,WAAW,IAAI;EACjC,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK;EACjC,QAAQ,UAAU,oBAAC,sBAAD;GAAsB,GAAI;GAAkB;GAAiB;EAAO,CAAA;CACxF;AACF;;;ACzFA,SAAS,UAAU,MAAsB;CACvC,OAAO,OAAO,OACV,GAAG,KAAK,MACR,OAAO,UACL,GAAG,KAAK,KAAK,OAAO,IAAI,EAAE,OAC1B,IAAI,OAAQ,QAAA,CAAc,QAAQ,CAAC,EAAE;AAC7C;AAEA,SAAgB,oBAAoB,EAAE,OAAO,SAAS,OAAO,UAA8B;CACzF,OACE,qBAAC,OAAD;EAAK,eAAY;EAAyB,WAAU;EAApD,UAAA;GACG,QACC,oBAAC,KAAD;IAAG,MAAK;IAAQ,WAAU;IACvB,UAAA;GACA,CAAA,IACD;GACH,UACC,oBAAC,KAAD;IAAG,MAAK;IAAS,WAAU;IAAwB,UAAA;GAEhD,CAAA,IACD;GACH,CAAC,WAAW,CAAC,SAAS,MAAM,WAAW,IAAI,oBAAC,KAAD;IAAG,WAAU;IAAwB,UAAA;GAAmB,CAAA,IAAI;GACvG,gBAAgB,KAAK,CAAC,CACpB,MAAM,GAAG,CAAC,CAAC,CACX,KAAK,SACJ,qBAAC,QAAD;IAEE,SAAQ;IACR,MAAK;IACL,sBAAoB,KAAK;IACzB,eAAe,OAAO,IAAI;IAC1B,WAAU;IANZ,UAAA,CAQE,qBAAC,QAAD;KAAM,WAAU;KAAhB,UAAA,CACE,oBAAC,UAAD;MAAU,eAAA;MAAY,WAAU;KAA0B,CAAA,GAC1D,oBAAC,QAAD;MAAM,WAAU;MAAY,UAAA,KAAK;KAAW,CAAA,CACxC;IACN,CAAA,GAAA,oBAAC,QAAD;KAAM,WAAU;KAA2C,OAAO,KAAK;KACpE,UAAA,KAAK;IACF,CAAA,CACA;GAdD,GAAA,KAAK,IAcJ,CACT;EACA;;AAET;AAEA,SAAgB,iBAAiB,EAAE,OAAO,SAAS,OAAO,UAA8B;CACtF,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,qBAAC,UAAD;IAAQ,WAAU;IAAlB,UAAA,CACE,oBAAC,MAAD;KAAI,WAAU;KAAqC,UAAA;IAAa,CAAA,GAChE,qBAAC,QAAD;KAAM,eAAY;KAAkB,WAAU;KAA9C,UAAA;MACG,MAAM;MAAO;MAAE,MAAM,WAAW,IAAI,aAAa;KAC9C;IACA,CAAA,CAAA;;GACP,QACC,oBAAC,KAAD;IAAG,MAAK;IAAQ,WAAU;IACvB,UAAA;GACA,CAAA,IACD;GACH,UACC,oBAAC,KAAD;IAAG,MAAK;IAAS,WAAU;IAAkC,UAAA;GAE1D,CAAA,IACD;GACH,CAAC,WAAW,CAAC,SAAS,MAAM,WAAW,IACtC,oBAAC,YAAD;IACE,OAAM;IACN,aAAY;IACZ,WAAU;GACX,CAAA,IACC;GACJ,oBAAC,OAAD;IAAK,eAAY;IAAiB,WAAU;IACzC,UAAA,gBAAgB,KAAK,CAAC,CAAC,KAAK,SAC3B,qBAAC,QAAD;KAEE,SAAQ;KACR,MAAK;KACL,sBAAoB,KAAK;KACzB,eAAe,OAAO,IAAI;KAC1B,WAAU;KANZ,UAAA;MAQE,oBAAC,UAAD;OAAU,eAAA;OAAY,WAAU;MAA0B,CAAA;MAC1D,oBAAC,QAAD;OAAM,WAAU;OAAgD,OAAO,KAAK;OACzE,UAAA,KAAK;MACF,CAAA;MACN,oBAAC,QAAD;OAAM,WAAU;OAA2C,OAAO,KAAK;OACpE,UAAA,KAAK;MACF,CAAA;MACN,oBAAC,QAAD;OAAM,WAAU;OAAyB,UAAA,UAAU,KAAK,IAAI;MAAQ,CAAA;KAC9D;IAfD,GAAA,KAAK,IAeJ,CACT;GACE,CAAA;EACF;;AAET;;;AClGA,SAAgB,eAA6B;CAE3C,OAAO;EAAE,IAAI;EAAkB,OAAO;EAAa,OAAO;CAAe;AAC3E;AAEA,SAAgB,yBAAyB,EAAE,WAAW,oBAAwC;CAC5F,MAAM,WAAW,SAAS,UAAU,QAAQ,UAAU,UAAU,OAAO,OAAO,SAAS,CAAC;CACxF,gBAAgB;EACd,IAAI,cAAc,QAAQ,SAAS,QAAQ;EAC3C,MAAM,aAAa,IAAI,gBAAgB;EACvC,iBAAsB,WAAW,WAAW,MAAM;EAClD,aAAa,WAAW,MAAM;CAChC,GAAG,CAAC,WAAW,SAAS,MAAM,CAAC;CAC/B,OACE,oBAAC,qBAAD;EACE,OAAO,SAAS;EAChB,SAAS,CAAC,SAAS;EACnB,OAAO,SAAS;EAChB,SAAS,SAAS;GAChB,IAAI,cAAc,MAAM,iBAAiB,YAAY,WAAW,KAAK,IAAI,CAAC;EAC5E;CACD,CAAA;AAEL;AAEA,SAAgB,eAAe,EAAE,WAAW,oBAAwC;CAClF,MAAM,WAAW,SAAS,UAAU,QAAQ,UAAU,UAAU,OAAO,OAAO,SAAS,CAAC;CACxF,gBAAgB;EACd,IAAI,cAAc,MAAM;EACxB,MAAM,aAAa,IAAI,gBAAgB;EACvC,iBAAsB,WAAW,WAAW,MAAM;EAClD,aAAa,WAAW,MAAM;CAChC,GAAG,CAAC,SAAS,CAAC;CACd,OACE,oBAAC,kBAAD;EACE,OAAO,SAAS;EAChB,SAAS,CAAC,SAAS,UAAU,cAAc;EAC3C,OAAO,cAAc,OAAO,gCAAgC,SAAS;EACrE,SAAS,SAAS;GAChB,IAAI,cAAc,MAAM,iBAAiB,YAAY,WAAW,KAAK,IAAI,CAAC;EAC5E;CACD,CAAA;AAEL;;;AC/CA,IAAA,0BAAe,oBAAoB;CACjC,MAAM;CACN,MAAM;CACN,WAAW;CACX,cAAc;CACd,cAAc;CACd,qBAAqB;CACrB,OAAO;AACT,CAAC;;;ACVD,IAAA,wBAAe,kBAAkB,gBAAgB;;;ACAjD,IAAA,2CAAe,WAAW,EAAE,WAAW,yBAAyB,CAAC;;;ACIjE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,YAAY,gBAAgB;CACvC,IAAI;CACJ,SAAS;EACP,IAAI,iBAA2D;GAAE,OAAO,CAAC,IAAI,EAAE,MAAM,YAAY,GAAGA,uBAAsB,CAAC;EAAG;EAC9H,IAAI,WAA+C;GAAE,OAAO,CAAC,IAAI,EAAE,SAAS,YAAY,GAAGC,qBAAgB,CAAC;EAAG;EAC/G,IAAI,QAAyC;GAAE,OAAO,CAAC,IAAI;IAAE,MAAM;IAAsB,IAAI;GAAY,GAAGC,wCAAa,CAAC;EAAG;CAC/H;AACF,CAAC"}
1
+ {"version":3,"file":"web.mjs","names":["activityGroupArtifacts","channelArtifacts","fillArtifacts"],"sources":["../../src/types/apiRoutes.ts","../../generated/client.ts","../../src/types/artifacts.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactView.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactsStore.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/artifactsApi.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewView.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactPreviewPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsView.tsx","../../src/extensions/workspaces/sessions/(frontend)/_components/ArtifactsPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web.ts","../../src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.web.tsx","../../generated/web.ts"],"sourcesContent":["import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { ArtifactPreview, ArtifactSnapshot } from './artifacts';\n\nconst routes = {\n list: {\n method: 'GET',\n path: '/',\n response: undefined as unknown as ApiResponseOf<ArtifactSnapshot>,\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: undefined as unknown as ApiResponseOf<ArtifactPreview>,\n },\n content: {\n method: 'GET',\n path: '/content',\n query: ['path'],\n raw: true,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\nimport { sealedTransport } from '@agimon-ai/po-web-security/browser';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'artifacts' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'artifacts',\n transport: (input, init) => sealedTransport.fetch(input, init),\n sessionAddress: sessionApiAddress,\n});\n","export interface ArtifactItem {\n /** Relative to the current session's artifact directory. */\n path: string;\n name: string;\n size: number;\n modifiedAt: number;\n}\n\nexport interface ArtifactSnapshot {\n items: ArtifactItem[];\n error?: string;\n}\n\nexport interface ArtifactPreview {\n path: string;\n content: string;\n mimeType: string;\n unavailable?: boolean;\n reason?: string;\n}\n\nexport const artifactsChannelType = 'artifacts';\n","import type { ArtifactItem, ArtifactSnapshot } from '../../../../../types/artifacts';\n\nexport function sortedArtifacts(items: readonly ArtifactItem[]): ArtifactItem[] {\n return [...items].sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n}\n\nexport function artifactTabId(sessionId: string, path: string): string {\n return `artifact:${encodeURIComponent(sessionId)}:${encodeURIComponent(path)}`;\n}\n\nexport function artifactsTabId(sessionId: string): string {\n return `artifacts-${encodeURIComponent(sessionId)}`;\n}\n\n/** Resolves document assets within the artifact folder, never into another filesystem root. */\nexport function artifactAssetPath(documentPath: string, imagePath: string): string {\n if (!imagePath || imagePath.startsWith('/') || imagePath.includes('\\\\') || /^[a-z][a-z0-9+.-]*:/iu.test(imagePath))\n throw new Error('Artifact images must use a relative path.');\n const segments = documentPath.split('/').slice(0, -1);\n for (const segment of imagePath.split('/')) {\n if (segment === '' || segment === '.') continue;\n if (segment === '..') {\n if (segments.length === 0) throw new Error('The image is outside this artifact folder.');\n segments.pop();\n } else segments.push(segment);\n }\n if (segments.length === 0) throw new Error('The image path is empty.');\n return segments.join('/');\n}\n\nexport function artifactPreviewMode(path: string): 'markdown' | 'html' | 'media' | 'code' | 'text' {\n if (/\\.(?:png|jpe?g|gif|webp|avif|bmp|svg|ico|mp3|wav|m4a|ogg|mp4|mov|webm|pdf)$/iu.test(path)) return 'media';\n if (/\\.(?:md|markdown)$/iu.test(path)) return 'markdown';\n if (/\\.html?$/iu.test(path)) return 'html';\n if (/\\.(?:[cm]?[jt]sx?|json|ya?ml|css|scss|py|sh|bash|go|rs|rb|java|c|cpp|h|sql|xml|toml)$/iu.test(path))\n return 'code';\n return 'text';\n}\n\n/** A malformed channel frame must not replace the current session's known inventory. */\nexport function parseArtifactSnapshot(input: unknown): ArtifactSnapshot | null {\n if (typeof input !== 'object' || input === null || !('items' in input) || !Array.isArray(input.items)) return null;\n if ('error' in input && input.error !== undefined && typeof input.error !== 'string') return null;\n const items: ArtifactItem[] = [];\n for (const item of input.items) {\n if (\n typeof item !== 'object' ||\n item === null ||\n typeof item.path !== 'string' ||\n !item.path ||\n item.path.startsWith('/') ||\n item.path.includes('\\\\') ||\n item.path.split('/').some((part: string) => part === '..' || part === '.') ||\n typeof item.name !== 'string' ||\n !item.name ||\n typeof item.size !== 'number' ||\n !Number.isFinite(item.size) ||\n item.size < 0 ||\n typeof item.modifiedAt !== 'number' ||\n !Number.isFinite(item.modifiedAt) ||\n item.modifiedAt < 0\n )\n return null;\n items.push({ path: item.path, name: item.name, size: item.size, modifiedAt: item.modifiedAt });\n }\n return {\n items: sortedArtifacts(items),\n ...('error' in input && typeof input.error === 'string' ? { error: input.error } : {}),\n };\n}\n","import { defineSessionStore } from '@agimon-ai/po-core/web';\n\nimport { artifactsChannelType, type ArtifactSnapshot } from '../../../../../types/artifacts';\nimport { parseArtifactSnapshot } from './artifactView';\n\nexport const artifacts = defineSessionStore<ArtifactSnapshot & { loaded: boolean }>({ items: [], loaded: false });\n\nexport const artifactsChannel = artifacts.channel<ArtifactSnapshot>({\n channel: artifactsChannelType,\n parse: parseArtifactSnapshot,\n reduce: (_current, snapshot) => ({ ...snapshot, loaded: true }),\n});\n\n/** Artifact documents are outcomes, not background work. The source also keeps the empty launcher visible. */\nexport const artifactActivitySource = {\n subscribe(listener: () => void) {\n const subscription = artifacts.store.subscribe(listener);\n return () => subscription.unsubscribe();\n },\n isActive: () => false,\n};\n","import { api } from '../../../../../../generated/client';\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifacts } from './artifactsStore';\nimport { artifactAssetPath, parseArtifactSnapshot } from './artifactView';\n\nexport async function refreshArtifacts(sessionId: string, signal: AbortSignal): Promise<void> {\n const before = artifacts.select(artifacts.store.state, sessionId);\n try {\n const result = await api.session(sessionId).list({ signal });\n if (signal.aborted || artifacts.select(artifacts.store.state, sessionId) !== before) return;\n const snapshot = result.ok ? parseArtifactSnapshot(result.data) : null;\n artifacts.update(sessionId, () =>\n snapshot\n ? { ...snapshot, loaded: true }\n : {\n ...before,\n loaded: true,\n error: result.ok\n ? 'The session answered with no artifact list.'\n : result.error || 'Artifacts could not be loaded.',\n },\n );\n } catch (error) {\n if (!signal.aborted && artifacts.select(artifacts.store.state, sessionId) === before)\n artifacts.update(sessionId, () => ({\n ...before,\n loaded: true,\n error: error instanceof Error ? error.message : String(error),\n }));\n }\n}\n\nexport async function fetchArtifactPreview(\n sessionId: string,\n path: string,\n signal: AbortSignal,\n): Promise<ArtifactPreview> {\n const result = await api.session(sessionId).preview({ query: { path }, signal });\n if (!result.ok) throw new Error(result.error || 'This artifact could not be read.');\n const preview = result.data;\n if (\n !preview ||\n typeof preview.path !== 'string' ||\n typeof preview.content !== 'string' ||\n typeof preview.mimeType !== 'string'\n )\n throw new Error('The session answered with no artifact.');\n return preview;\n}\n\n/** Generated raw calls keep media and Markdown asset reads on the sealed artifact transport. */\nexport async function artifactBlob(sessionId: string, path: string, signal?: AbortSignal): Promise<Blob> {\n const result = await api.session(sessionId).content({ query: { path }, ...(signal ? { signal } : {}) });\n if (!result.ok) throw new Error(result.error || 'This artifact could not be loaded.');\n return result.response.blob();\n}\n\nexport async function loadArtifactImage(sessionId: string, documentPath: string, imagePath: string) {\n const blob = await artifactBlob(sessionId, artifactAssetPath(documentPath, imagePath));\n if (!blob.type.startsWith('image/')) throw new Error('This artifact asset is not an image.');\n const url = URL.createObjectURL(blob);\n return { url, dispose: () => URL.revokeObjectURL(url) };\n}\n","import { Breadcrumb, CodeEditor, Markdown } from '@agimon-ai/po-web-components';\nimport type { ReactNode } from 'react';\n\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifactPreviewMode } from '../_lib/artifactView';\n\ninterface ArtifactPreviewViewProps {\n path: string;\n preview?: ArtifactPreview;\n error?: string;\n media?: ReactNode;\n loadImage?: (path: string) => Promise<{ url: string; dispose: () => void }>;\n}\n\nexport function ArtifactPreviewView({ path, preview, error, media, loadImage }: ArtifactPreviewViewProps) {\n const mode = artifactPreviewMode(path);\n return (\n <div\n data-testid=\"artifact-preview\"\n data-artifact-path={path}\n className=\"flex min-h-0 flex-1 flex-col bg-po-bg text-po-text\"\n >\n <header className=\"flex shrink-0 items-center gap-2 border-b border-po-border px-4 py-3\">\n <Breadcrumb path={path} className=\"min-w-0 flex-1\" />\n <span className=\"text-sm text-po-dim\">Artifact</span>\n </header>\n <div className=\"min-h-0 flex-1 overflow-auto p-4\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-po-red\">\n {error}\n </p>\n ) : !preview ? (\n <p role=\"status\" className=\"text-sm text-po-dim\">\n Reading this artifact...\n </p>\n ) : mode === 'media' ? (\n media\n ) : preview.unavailable ? (\n <p className=\"text-sm text-po-dim\">{preview.reason ?? 'This artifact cannot be previewed.'}</p>\n ) : mode === 'markdown' ? (\n <Markdown text={preview.content} loadImage={loadImage} />\n ) : mode === 'html' ? (\n <iframe\n title={path}\n sandbox=\"\"\n srcDoc={preview.content}\n className=\"h-96 w-full rounded-md border border-po-border bg-po-deep\"\n />\n ) : mode === 'code' ? (\n <CodeEditor\n value={preview.content}\n path={path}\n readOnly\n className=\"min-h-64 rounded-md border border-po-border\"\n />\n ) : (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-sm text-po-text\">{preview.content}</pre>\n )}\n </div>\n </div>\n );\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { MediaPreview } from '@agimon-ai/po-web-components';\nimport { useStore } from '@tanstack/react-store';\nimport { useCallback, useEffect, useState } from 'react';\n\nimport type { ArtifactPreview } from '../../../../../types/artifacts';\nimport { artifactBlob, fetchArtifactPreview, loadArtifactImage } from '../_lib/artifactsApi';\nimport { artifacts } from '../_lib/artifactsStore';\nimport { artifactTabId } from '../_lib/artifactView';\nimport { ArtifactPreviewView } from './ArtifactPreviewView';\n\nfunction ArtifactMedia({ sessionId, path, revision }: { sessionId: string; path: string; revision: string }) {\n const [loaded, setLoaded] = useState<{ source: string; url?: string; mimeType?: string; error?: string }>();\n const source = `${sessionId}:${path}:${revision}`;\n useEffect(() => {\n const controller = new AbortController();\n let url: string | undefined;\n void artifactBlob(sessionId, path, controller.signal)\n .then((blob) => {\n if (controller.signal.aborted) return;\n url = URL.createObjectURL(blob);\n setLoaded({ source, url, mimeType: blob.type });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setLoaded({ source, error: error instanceof Error ? error.message : String(error) });\n });\n return () => {\n controller.abort();\n if (url !== undefined) URL.revokeObjectURL(url);\n };\n }, [sessionId, path, source]);\n const current = loaded?.source === source ? loaded : undefined;\n return current?.url ? (\n <MediaPreview src={current.url} path={path} contentType={current.mimeType} />\n ) : (\n <p\n role={current?.error ? 'alert' : 'status'}\n className={current?.error ? 'text-sm text-po-red' : 'text-sm text-po-dim'}\n >\n {current?.error ?? 'Loading artifact media...'}\n </p>\n );\n}\n\nexport function ArtifactPreviewPanel({ path, sessionId }: WebPluginSlotProps & { path: string }) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n const item = snapshot.items.find((candidate) => candidate.path === path);\n const revision = item ? `${item.modifiedAt}:${item.size}` : snapshot.loaded ? 'missing' : 'initial';\n const [loaded, setLoaded] = useState<{\n sessionId: string;\n path: string;\n revision: string;\n preview?: ArtifactPreview;\n error?: string;\n }>();\n useEffect(() => {\n if (sessionId === null || revision === 'missing') return;\n const controller = new AbortController();\n void fetchArtifactPreview(sessionId, path, controller.signal)\n .then((preview) => {\n if (!controller.signal.aborted) setLoaded({ sessionId, path, revision, preview });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setLoaded({ sessionId, path, revision, error: error instanceof Error ? error.message : String(error) });\n });\n return () => controller.abort();\n }, [sessionId, path, revision]);\n const current =\n loaded?.sessionId === sessionId && loaded.path === path && loaded.revision === revision ? loaded : undefined;\n const loadImage = useCallback(\n (imagePath: string) => {\n if (sessionId === null) return Promise.reject(new Error('The session is unavailable.'));\n return loadArtifactImage(sessionId, path, imagePath);\n },\n [sessionId, path],\n );\n return (\n <ArtifactPreviewView\n path={path}\n preview={current?.preview}\n error={\n sessionId === null\n ? 'The session is unavailable.'\n : revision === 'missing'\n ? 'This artifact no longer exists.'\n : current?.error\n }\n loadImage={loadImage}\n media={sessionId === null ? undefined : <ArtifactMedia sessionId={sessionId} path={path} revision={revision} />}\n />\n );\n}\n\nexport function artifactTab(sessionId: string, path: string): TransientTab {\n return {\n id: artifactTabId(sessionId, path),\n label: path.split('/').at(-1) ?? path,\n panel: (props) => <ArtifactPreviewPanel {...props} sessionId={sessionId} path={path} />,\n };\n}\n","import { Button, EmptyState, FileIcon } from '@agimon-ai/po-web-components';\n\nimport type { ArtifactItem } from '../../../../../types/artifacts';\nimport { sortedArtifacts } from '../_lib/artifactView';\n\ninterface ArtifactsViewProps {\n items: readonly ArtifactItem[];\n loading?: boolean;\n error?: string;\n onOpen: (item: ArtifactItem) => void;\n}\n\nfunction sizeLabel(size: number): string {\n return size < 1024\n ? `${size} B`\n : size < 1024 * 1024\n ? `${Math.ceil(size / 1024)} KB`\n : `${(size / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport function RecentArtifactsView({ items, loading, error, onOpen }: ArtifactsViewProps) {\n return (\n <div data-testid=\"artifacts-recent-items\" className=\"flex flex-col gap-1\">\n {error ? (\n <p role=\"alert\" className=\"text-sm text-po-red\">\n {error}\n </p>\n ) : null}\n {loading ? (\n <p role=\"status\" className=\"text-sm text-po-dim\">\n Loading artifacts...\n </p>\n ) : null}\n {!loading && !error && items.length === 0 ? <p className=\"text-sm text-po-dim\">No artifacts yet</p> : null}\n {sortedArtifacts(items)\n .slice(0, 3)\n .map((item) => (\n <Button\n key={item.path}\n variant=\"ghost\"\n size=\"card\"\n data-artifact-path={item.path}\n onClick={() => onOpen(item)}\n className=\"gap-0.5 px-2 py-1.5\"\n >\n <span className=\"flex min-w-0 items-center gap-2 text-base text-po-text\">\n <FileIcon aria-hidden className=\"size-3.5 text-po-dim\" />\n <span className=\"truncate\">{item.name}</span>\n </span>\n <span className=\"truncate font-mono text-sm text-po-dim\" title={item.path}>\n {item.path}\n </span>\n </Button>\n ))}\n </div>\n );\n}\n\nexport function ArtifactGridView({ items, loading, error, onOpen }: ArtifactsViewProps) {\n return (\n <div className=\"flex min-h-0 flex-1 flex-col overflow-auto bg-po-bg text-po-text\">\n <header className=\"flex shrink-0 items-center gap-3 border-b border-po-border px-4 py-3\">\n <h1 className=\"text-lg font-semibold text-po-hi\">Artifacts</h1>\n <span data-testid=\"artifacts-count\" className=\"text-sm text-po-dim\">\n {items.length} {items.length === 1 ? 'artifact' : 'artifacts'}\n </span>\n </header>\n {error ? (\n <p role=\"alert\" className=\"px-4 py-3 text-sm text-po-red\">\n {error}\n </p>\n ) : null}\n {loading ? (\n <p role=\"status\" className=\"px-4 py-3 text-sm text-po-dim\">\n Loading artifacts...\n </p>\n ) : null}\n {!loading && !error && items.length === 0 ? (\n <EmptyState\n title=\"No artifacts yet\"\n description=\"Documents Po prepares for this session will appear here.\"\n className=\"px-4 py-8\"\n />\n ) : null}\n <div data-testid=\"artifacts-grid\" className=\"grid grid-cols-1 gap-3 p-4 sm:grid-cols-2 xl:grid-cols-3\">\n {sortedArtifacts(items).map((item) => (\n <Button\n key={item.path}\n variant=\"outline\"\n size=\"card\"\n data-artifact-path={item.path}\n onClick={() => onOpen(item)}\n className=\"min-w-0 gap-2 p-4\"\n >\n <FileIcon aria-hidden className=\"size-5 text-po-green\" />\n <span className=\"truncate text-base font-semibold text-po-hi\" title={item.name}>\n {item.name}\n </span>\n <span className=\"truncate font-mono text-sm text-po-dim\" title={item.path}>\n {item.path}\n </span>\n <span className=\"text-sm text-po-dim\">{sizeLabel(item.size)}</span>\n </Button>\n ))}\n </div>\n </div>\n );\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { useStore } from '@tanstack/react-store';\nimport { useEffect } from 'react';\n\nimport { refreshArtifacts } from '../_lib/artifactsApi';\nimport { artifacts } from '../_lib/artifactsStore';\nimport { artifactTab } from './ArtifactPreviewPanel';\nimport { ArtifactGridView, RecentArtifactsView } from './ArtifactsView';\n\nexport function artifactsTab(): TransientTab {\n // The host stores transient tabs per session, including this heading factory's fixed id.\n return { id: 'artifacts-grid', label: 'Artifacts', panel: ArtifactsPanel };\n}\n\nexport function ArtifactsActivitySection({ sessionId, openTransientTab }: WebPluginSlotProps) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n useEffect(() => {\n if (sessionId === null || snapshot.loaded) return;\n const controller = new AbortController();\n void refreshArtifacts(sessionId, controller.signal);\n return () => controller.abort();\n }, [sessionId, snapshot.loaded]);\n return (\n <RecentArtifactsView\n items={snapshot.items}\n loading={!snapshot.loaded}\n error={snapshot.error}\n onOpen={(item) => {\n if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));\n }}\n />\n );\n}\n\nexport function ArtifactsPanel({ sessionId, openTransientTab }: WebPluginSlotProps) {\n const snapshot = useStore(artifacts.store, (state) => artifacts.select(state, sessionId));\n useEffect(() => {\n if (sessionId === null) return;\n const controller = new AbortController();\n void refreshArtifacts(sessionId, controller.signal);\n return () => controller.abort();\n }, [sessionId]);\n return (\n <ArtifactGridView\n items={snapshot.items}\n loading={!snapshot.loaded && sessionId !== null}\n error={sessionId === null ? 'The session is unavailable.' : snapshot.error}\n onOpen={(item) => {\n if (sessionId !== null) openTransientTab(artifactTab(sessionId, item.path));\n }}\n />\n );\n}\n","import { defineActivityGroup } from '@agimon-ai/po-core/web';\nimport { FileIcon } from '@agimon-ai/po-web-components';\n\nimport { artifactsTab } from '../_components/ArtifactsPanel';\nimport { artifactActivitySource } from '../_lib/artifactsStore';\nexport default defineActivityGroup({\n icon: FileIcon,\n keys: '',\n statusKey: 'po-artifacts',\n activeSource: artifactActivitySource,\n transientTab: artifactsTab,\n marksBackgroundWork: false,\n order: 15,\n});\n","import { defineChannelFile } from '@agimon-ai/po-core/web';\n\nimport { artifactsChannel } from '../_lib/artifactsStore';\nexport default defineChannelFile(artifactsChannel);\n","import { defineFill } from '@agimon-ai/po-core/web';\n\nimport { ArtifactsActivitySection } from '../_components/ArtifactsPanel';\nexport default defineFill({ component: ArtifactsActivitySection });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/po-core/web';\n\nimport activityGroupArtifacts from '../src/extensions/workspaces/sessions/(frontend)/activity-group/artifacts.web';\nimport channelArtifacts from '../src/extensions/workspaces/sessions/(frontend)/channel/artifacts.web';\nimport fillArtifacts from '../src/extensions/workspaces/sessions/(frontend)/fill/Artifacts.activity.artifacts.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'artifacts',\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'artifacts' }, activityGroupArtifacts)]; },\n get channels(): WebPluginContributions['channels'] { return [via({ channel: 'artifacts' }, channelArtifacts)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'activity.artifacts', id: 'artifacts' }, fillArtifacts)]; },\n },\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/po-artifact",
3
- "version": "0.0.0-alpha.2",
3
+ "version": "0.0.0-alpha.3",
4
4
  "description": "Durable session artifacts, prompt guidance, and web/native artifact views.",
5
5
  "keywords": [
6
6
  "ai",
@@ -65,37 +65,37 @@
65
65
  "registry": "https://registry.npmjs.org/"
66
66
  },
67
67
  "dependencies": {
68
- "@agimon-ai/po-core": "0.0.0-alpha.3",
69
- "@agimon-ai/po-web-components": "0.0.0-alpha.3",
70
- "@agimon-ai/po-web-security": "0.0.0-alpha.3",
68
+ "@agimon-ai/po-core": "0.0.0-alpha.4",
69
+ "@agimon-ai/po-web-components": "0.0.0-alpha.4",
70
+ "@agimon-ai/po-web-security": "0.0.0-alpha.4",
71
71
  "@deepseek-ai/cordis": "4.0.4",
72
72
  "hono": "4.13.13",
73
- "typebox": "1.3.36"
73
+ "typebox": "1.3.37"
74
74
  },
75
75
  "devDependencies": {
76
- "@agimon-ai/po-build": "0.0.0-alpha.3",
77
- "@agimon-ai/po-native-components": "0.0.0-alpha.3",
78
- "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.0-alpha.3",
76
+ "@agimon-ai/po-build": "0.0.0-alpha.4",
77
+ "@agimon-ai/po-native-components": "0.0.0-alpha.4",
78
+ "@agimon-ai/vibe-lint-plugin-po-extension": "0.0.0-alpha.4",
79
79
  "@earendil-works/pi-coding-agent": "1.2.0",
80
80
  "@tanstack/react-store": "0.11.2",
81
81
  "@tanstack/store": "0.11.2",
82
- "@types/node": "25.9.9",
82
+ "@types/node": "26.6.5",
83
83
  "@types/react": "19.3.0",
84
84
  "@types/react-dom": "19.3.0",
85
- "@vitest/coverage-v8": "5.0.3",
85
+ "@vitest/coverage-v8": "5.0.4",
86
86
  "react": "19.3.0",
87
87
  "react-dom": "19.3.0",
88
- "react-native": "0.87.1",
89
- "react-native-unistyles": "3.5.2",
88
+ "react-native": "0.86.3",
89
+ "react-native-unistyles": "3.5.3",
90
90
  "tsdown": "0.23.1",
91
91
  "typescript": "7.0.2",
92
- "vitest": "5.0.3"
92
+ "vitest": "5.0.4"
93
93
  },
94
94
  "peerDependencies": {
95
- "@agimon-ai/po-native-components": "0.0.0-alpha.3",
95
+ "@agimon-ai/po-native-components": "0.0.0-alpha.4",
96
96
  "@earendil-works/pi-coding-agent": "1.2.0",
97
- "react-native": "0.87.1",
98
- "react-native-unistyles": "3.5.2"
97
+ "react-native": "0.86.3",
98
+ "react-native-unistyles": "^3.5.3"
99
99
  },
100
100
  "peerDependenciesMeta": {
101
101
  "@earendil-works/pi-coding-agent": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"artifactFiles-B8w_HQXv.mjs","names":[],"sources":["../src/constants/artifacts.ts","../src/services/artifactFiles/artifactPaths.ts","../src/services/artifactFiles/index.ts"],"sourcesContent":["export const ARTIFACT_DIRECTORY = 'doom-artifacts';\nexport const MAX_PREVIEW_BYTES = 1024 * 1024;\nexport const MAX_CONTENT_BYTES = 25 * 1024 * 1024;\nexport const ARTIFACT_REFRESH_MS = 2000;\n\nexport const ARTIFACT_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/vnd.microsoft.icon',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.m4a': 'audio/mp4',\n '.ogg': 'audio/ogg',\n '.m4v': 'video/mp4',\n '.mov': 'video/quicktime',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.pdf': 'application/pdf',\n '.md': 'text/markdown; charset=utf-8',\n '.markdown': 'text/markdown; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n};\n","import path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport { ARTIFACT_CONTENT_TYPES, ARTIFACT_DIRECTORY } from '../../constants/artifacts';\n\nexport class ArtifactAccessError extends Error {\n constructor(\n message: string,\n readonly status: 400 | 403 | 404 | 413,\n ) {\n super(message);\n }\n}\n\nexport function artifactRoot(sessionId: string, environment: Readonly<Record<string, string | undefined>>): string {\n if (!/^[a-zA-Z0-9_-]+$/u.test(sessionId)) throw new ArtifactAccessError('Invalid artifact session identity.', 400);\n return path.join(piAgentDirectory(environment, environment.HOME), ARTIFACT_DIRECTORY, sessionId);\n}\n\nexport function artifactMimeType(relativePath: string): string {\n return ARTIFACT_CONTENT_TYPES[path.extname(relativePath).toLowerCase()] ?? 'application/octet-stream';\n}\n","import { constants } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { ArtifactItem, ArtifactSnapshot } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactMimeType } from './artifactPaths';\n\nexport { ArtifactAccessError, artifactMimeType, artifactRoot } from './artifactPaths';\n\nfunction inside(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\nfunction codeOf(error: unknown): string | undefined {\n return error instanceof Error && 'code' in error ? String(error.code) : undefined;\n}\n\n/** The owned directories must not redirect artifact access through a symlink. */\nasync function checkedRoot(root: string): Promise<string> {\n const base = path.dirname(root);\n for (const directory of [base, root]) {\n const stat = await fs.lstat(directory);\n if (!stat.isDirectory() || stat.isSymbolicLink())\n throw new ArtifactAccessError('The artifact folder is unavailable.', 403);\n }\n const realBase = await fs.realpath(base);\n const realRoot = await fs.realpath(root);\n if (!inside(realBase, realRoot))\n throw new ArtifactAccessError('The artifact folder is outside its storage directory.', 403);\n return realRoot;\n}\n\nexport async function ensureArtifactRoot(root: string): Promise<void> {\n await fs.mkdir(path.dirname(root), { recursive: true, mode: 0o700 });\n await fs.mkdir(root, { recursive: true, mode: 0o700 });\n await checkedRoot(root);\n}\n\n/** Open without following mutable ancestors. Node exposes no portable openat API. */\nasync function openContained(root: string, segments: string[]): Promise<fs.FileHandle> {\n if (process.platform === 'darwin') {\n // Darwin O_NOFOLLOW_ANY checks every component atomically. Do not combine it with O_NOFOLLOW.\n return fs.open(path.join(root, ...segments), constants.O_RDONLY | constants.O_NONBLOCK | 0x20000000);\n }\n if (process.platform !== 'linux') throw new ArtifactAccessError('Secure artifact reads require macOS or Linux.', 403);\n const flags = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW;\n let directory = await fs.open(path.dirname(root), flags);\n try {\n for (const segment of [path.basename(root), ...segments.slice(0, -1)]) {\n const child = await fs.open(`/proc/self/fd/${directory.fd}/${segment}`, flags);\n await directory.close();\n directory = child;\n }\n return await fs.open(\n `/proc/self/fd/${directory.fd}/${segments.at(-1)!}`,\n constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,\n );\n } finally {\n await directory.close();\n }\n}\n\n/** One guarded open shared by metadata, text and byte reads. The caller closes the handle. */\nexport async function openArtifact(root: string, requested: string | undefined) {\n if (\n !requested ||\n requested.includes('\\0') ||\n requested.includes('\\\\') ||\n path.isAbsolute(requested) ||\n requested.split('/').some((part) => part === '..' || part === '.' || part === '')\n )\n throw new ArtifactAccessError('Invalid artifact path.', 400);\n try {\n const realRoot = await checkedRoot(root);\n const candidate = path.resolve(realRoot, requested);\n if (!inside(realRoot, candidate))\n throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n let current = realRoot;\n const segments = requested.split('/');\n for (let index = 0; index < segments.length; index += 1) {\n current = path.join(current, segments[index]!);\n const stat = await fs.lstat(current);\n if (stat.isSymbolicLink()) throw new ArtifactAccessError('Artifact symlinks cannot be read.', 403);\n if (index < segments.length - 1 ? !stat.isDirectory() : !stat.isFile())\n throw new ArtifactAccessError('The artifact is not a regular file.', 404);\n }\n const real = await fs.realpath(candidate);\n if (!inside(realRoot, real)) throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n const expected = await fs.lstat(real);\n const handle = await openContained(realRoot, segments);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.dev !== expected.dev || stat.ino !== expected.ino)\n throw new ArtifactAccessError('The artifact changed while it was opened. Try again.', 403);\n return { handle, stat, path: requested, mimeType: artifactMimeType(requested) };\n } catch (error) {\n await handle.close();\n throw error;\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError) throw error;\n if (codeOf(error) === 'ENOENT' || codeOf(error) === 'ENOTDIR')\n throw new ArtifactAccessError('The artifact no longer exists.', 404);\n if (codeOf(error) === 'EACCES' || codeOf(error) === 'EPERM' || codeOf(error) === 'ELOOP')\n throw new ArtifactAccessError('The artifact cannot be read.', 403);\n throw error;\n }\n}\n\n/** Bound allocation and read length even when the file grows after its metadata was read. */\nexport async function readArtifactBytes(handle: fs.FileHandle, size: number, offset = 0): Promise<Uint8Array> {\n const bytes = new Uint8Array(size);\n let read = 0;\n while (read < size) {\n const result = await handle.read(bytes, read, size - read, offset + read);\n if (result.bytesRead === 0) break;\n read += result.bytesRead;\n }\n return bytes.subarray(0, read);\n}\n\nexport async function listArtifacts(root: string): Promise<ArtifactSnapshot> {\n const items: ArtifactItem[] = [];\n try {\n const realRoot = await checkedRoot(root);\n const visit = async (directory: string): Promise<void> => {\n for (const entry of await fs.readdir(directory, { withFileTypes: true })) {\n const location = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n // Recheck each directory rather than trusting a readdir entry across a rename.\n const stat = await fs.lstat(location);\n if (!stat.isDirectory() || stat.isSymbolicLink()) continue;\n const real = await fs.realpath(location);\n if (inside(realRoot, real)) await visit(real);\n } else if (entry.isFile()) {\n const relativePath = path.relative(realRoot, location).split(path.sep).join('/');\n try {\n const file = await openArtifact(root, relativePath);\n try {\n items.push({ path: relativePath, name: entry.name, size: file.stat.size, modifiedAt: file.stat.mtimeMs });\n } finally {\n await file.handle.close();\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError && (error.status === 404 || error.status === 403)) continue;\n throw error;\n }\n }\n }\n };\n await visit(realRoot);\n items.sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n return { items };\n } catch (error) {\n if (codeOf(error) === 'ENOENT') return { items: [] };\n return { items: [], error: error instanceof Error ? error.message : 'The artifact folder could not be read.' };\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"artifactFiles-Bo5Mhwq0.cjs","names":["path","piAgentDirectory","path","fs","constants"],"sources":["../src/constants/artifacts.ts","../src/services/artifactFiles/artifactPaths.ts","../src/services/artifactFiles/index.ts"],"sourcesContent":["export const ARTIFACT_DIRECTORY = 'doom-artifacts';\nexport const MAX_PREVIEW_BYTES = 1024 * 1024;\nexport const MAX_CONTENT_BYTES = 25 * 1024 * 1024;\nexport const ARTIFACT_REFRESH_MS = 2000;\n\nexport const ARTIFACT_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/vnd.microsoft.icon',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.m4a': 'audio/mp4',\n '.ogg': 'audio/ogg',\n '.m4v': 'video/mp4',\n '.mov': 'video/quicktime',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.pdf': 'application/pdf',\n '.md': 'text/markdown; charset=utf-8',\n '.markdown': 'text/markdown; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n};\n","import path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport { ARTIFACT_CONTENT_TYPES, ARTIFACT_DIRECTORY } from '../../constants/artifacts';\n\nexport class ArtifactAccessError extends Error {\n constructor(\n message: string,\n readonly status: 400 | 403 | 404 | 413,\n ) {\n super(message);\n }\n}\n\nexport function artifactRoot(sessionId: string, environment: Readonly<Record<string, string | undefined>>): string {\n if (!/^[a-zA-Z0-9_-]+$/u.test(sessionId)) throw new ArtifactAccessError('Invalid artifact session identity.', 400);\n return path.join(piAgentDirectory(environment, environment.HOME), ARTIFACT_DIRECTORY, sessionId);\n}\n\nexport function artifactMimeType(relativePath: string): string {\n return ARTIFACT_CONTENT_TYPES[path.extname(relativePath).toLowerCase()] ?? 'application/octet-stream';\n}\n","import { constants } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { ArtifactItem, ArtifactSnapshot } from '../../types/artifacts';\nimport { ArtifactAccessError, artifactMimeType } from './artifactPaths';\n\nexport { ArtifactAccessError, artifactMimeType, artifactRoot } from './artifactPaths';\n\nfunction inside(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\nfunction codeOf(error: unknown): string | undefined {\n return error instanceof Error && 'code' in error ? String(error.code) : undefined;\n}\n\n/** The owned directories must not redirect artifact access through a symlink. */\nasync function checkedRoot(root: string): Promise<string> {\n const base = path.dirname(root);\n for (const directory of [base, root]) {\n const stat = await fs.lstat(directory);\n if (!stat.isDirectory() || stat.isSymbolicLink())\n throw new ArtifactAccessError('The artifact folder is unavailable.', 403);\n }\n const realBase = await fs.realpath(base);\n const realRoot = await fs.realpath(root);\n if (!inside(realBase, realRoot))\n throw new ArtifactAccessError('The artifact folder is outside its storage directory.', 403);\n return realRoot;\n}\n\nexport async function ensureArtifactRoot(root: string): Promise<void> {\n await fs.mkdir(path.dirname(root), { recursive: true, mode: 0o700 });\n await fs.mkdir(root, { recursive: true, mode: 0o700 });\n await checkedRoot(root);\n}\n\n/** Open without following mutable ancestors. Node exposes no portable openat API. */\nasync function openContained(root: string, segments: string[]): Promise<fs.FileHandle> {\n if (process.platform === 'darwin') {\n // Darwin O_NOFOLLOW_ANY checks every component atomically. Do not combine it with O_NOFOLLOW.\n return fs.open(path.join(root, ...segments), constants.O_RDONLY | constants.O_NONBLOCK | 0x20000000);\n }\n if (process.platform !== 'linux') throw new ArtifactAccessError('Secure artifact reads require macOS or Linux.', 403);\n const flags = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW;\n let directory = await fs.open(path.dirname(root), flags);\n try {\n for (const segment of [path.basename(root), ...segments.slice(0, -1)]) {\n const child = await fs.open(`/proc/self/fd/${directory.fd}/${segment}`, flags);\n await directory.close();\n directory = child;\n }\n return await fs.open(\n `/proc/self/fd/${directory.fd}/${segments.at(-1)!}`,\n constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,\n );\n } finally {\n await directory.close();\n }\n}\n\n/** One guarded open shared by metadata, text and byte reads. The caller closes the handle. */\nexport async function openArtifact(root: string, requested: string | undefined) {\n if (\n !requested ||\n requested.includes('\\0') ||\n requested.includes('\\\\') ||\n path.isAbsolute(requested) ||\n requested.split('/').some((part) => part === '..' || part === '.' || part === '')\n )\n throw new ArtifactAccessError('Invalid artifact path.', 400);\n try {\n const realRoot = await checkedRoot(root);\n const candidate = path.resolve(realRoot, requested);\n if (!inside(realRoot, candidate))\n throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n let current = realRoot;\n const segments = requested.split('/');\n for (let index = 0; index < segments.length; index += 1) {\n current = path.join(current, segments[index]!);\n const stat = await fs.lstat(current);\n if (stat.isSymbolicLink()) throw new ArtifactAccessError('Artifact symlinks cannot be read.', 403);\n if (index < segments.length - 1 ? !stat.isDirectory() : !stat.isFile())\n throw new ArtifactAccessError('The artifact is not a regular file.', 404);\n }\n const real = await fs.realpath(candidate);\n if (!inside(realRoot, real)) throw new ArtifactAccessError('The artifact is outside this session folder.', 403);\n const expected = await fs.lstat(real);\n const handle = await openContained(realRoot, segments);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.dev !== expected.dev || stat.ino !== expected.ino)\n throw new ArtifactAccessError('The artifact changed while it was opened. Try again.', 403);\n return { handle, stat, path: requested, mimeType: artifactMimeType(requested) };\n } catch (error) {\n await handle.close();\n throw error;\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError) throw error;\n if (codeOf(error) === 'ENOENT' || codeOf(error) === 'ENOTDIR')\n throw new ArtifactAccessError('The artifact no longer exists.', 404);\n if (codeOf(error) === 'EACCES' || codeOf(error) === 'EPERM' || codeOf(error) === 'ELOOP')\n throw new ArtifactAccessError('The artifact cannot be read.', 403);\n throw error;\n }\n}\n\n/** Bound allocation and read length even when the file grows after its metadata was read. */\nexport async function readArtifactBytes(handle: fs.FileHandle, size: number, offset = 0): Promise<Uint8Array> {\n const bytes = new Uint8Array(size);\n let read = 0;\n while (read < size) {\n const result = await handle.read(bytes, read, size - read, offset + read);\n if (result.bytesRead === 0) break;\n read += result.bytesRead;\n }\n return bytes.subarray(0, read);\n}\n\nexport async function listArtifacts(root: string): Promise<ArtifactSnapshot> {\n const items: ArtifactItem[] = [];\n try {\n const realRoot = await checkedRoot(root);\n const visit = async (directory: string): Promise<void> => {\n for (const entry of await fs.readdir(directory, { withFileTypes: true })) {\n const location = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n // Recheck each directory rather than trusting a readdir entry across a rename.\n const stat = await fs.lstat(location);\n if (!stat.isDirectory() || stat.isSymbolicLink()) continue;\n const real = await fs.realpath(location);\n if (inside(realRoot, real)) await visit(real);\n } else if (entry.isFile()) {\n const relativePath = path.relative(realRoot, location).split(path.sep).join('/');\n try {\n const file = await openArtifact(root, relativePath);\n try {\n items.push({ path: relativePath, name: entry.name, size: file.stat.size, modifiedAt: file.stat.mtimeMs });\n } finally {\n await file.handle.close();\n }\n } catch (error) {\n if (error instanceof ArtifactAccessError && (error.status === 404 || error.status === 403)) continue;\n throw error;\n }\n }\n }\n };\n await visit(realRoot);\n items.sort(\n (left, right) =>\n right.modifiedAt - left.modifiedAt || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0),\n );\n return { items };\n } catch (error) {\n if (codeOf(error) === 'ENOENT') return { items: [] };\n return { items: [], error: error instanceof Error ? error.message : 'The artifact folder could not be read.' };\n }\n}\n"],"mappings":"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"}