pi-roundtable-web 0.7.4 → 0.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,10 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.7.5] - 2026-10-03
9
+
10
+ - Show a skill of any size, and add `skills.errorDetail` to show why one cannot be read.
11
+
8
12
  ## [0.7.4] - 2026-10-03
9
13
 
10
14
  - Show a `channel name unavailable` detail for a channel Discord cannot name.
package/README.md CHANGED
@@ -113,7 +113,7 @@ The host wrapper must declare any additional required service keys in its plugin
113
113
  | `party` | `contains(key)` separates party channels from owner transcripts; `list()` returns `PartyView[]` with channel, profile, enabler, enabled time, container state, busy count and optional last activity. |
114
114
  | `schedules` | `count(key)` returns the workspace schedule count. |
115
115
  | `cleanup` | `startFresh(key)` archives/waits for active turns and returns its kind; `deleteConversation(key)` returns `deleted` or `busy`. The package validates key shape, ownership, existence and exclusions before calling either hook. |
116
- | `skills` | `catalog()` supplies public `SkillView[]`; `read(name)` reads only that catalog entry and returns `{ frontmatter, body }`. The host bounds disk reads; the API caps body at 1 MB and frontmatter at 64 KB and flattens metadata in source order. |
116
+ | `skills` | `catalog()` supplies public `SkillView[]`; `read(name)` reads only that catalog entry and returns `{ frontmatter, body }`. The host bounds disk reads and the API shows what `read` returns, flattening metadata in source order. A skill that cannot be read answers with a fixed message; set `errorDetail: true` to append the catalog's `missing` text or the read error's message (cut at 300 characters) when the host's reasons are safe to show. |
117
117
  | `connectors` | `gateways()`, `servers()`, `usedBy(server)` provide status/tools/usage. Optional `adminUrl` accepts HTTP(S) or an absolute local path, never script URLs. |
118
118
 
119
119
  `sessionSummary(directory)` is a public helper for a trusted host to summarize separately stored party session files.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-roundtable-web",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "An owner-only web console for pi-roundtable: conversations, transcripts, and memory notes, with live updates",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -47,7 +47,7 @@
47
47
  "@types/bun": "1.4.2",
48
48
  "@types/react": "19.3.0",
49
49
  "@types/react-dom": "19.3.0",
50
- "pi-roundtable": "0.7.4",
50
+ "pi-roundtable": "0.7.5",
51
51
  "react": "19.3.0",
52
52
  "react-dom": "19.3.0",
53
53
  "react-markdown": "10.1.0",
@@ -108,7 +108,7 @@ export class ConsoleApi {
108
108
  if (error instanceof HttpError)
109
109
  return json(
110
110
  {
111
- error: message(this.#ports.presentation, error.message),
111
+ error: `${message(this.#ports.presentation, error.message)}${error.detail ?? ""}`,
112
112
  } satisfies ApiError,
113
113
  error.status,
114
114
  );
package/src/features.ts CHANGED
@@ -21,6 +21,12 @@ export interface ConsoleFeatures {
21
21
  read(
22
22
  name: string,
23
23
  ): Promise<{ frontmatter: Record<string, unknown>; body: string }>;
24
+ /**
25
+ * Show why a skill could not be read (the catalog's `missing` text, or the read error's
26
+ * message, cut at 300 characters) after the fixed message. Off by default: the reason may
27
+ * name a path.
28
+ */
29
+ errorDetail?: boolean;
24
30
  };
25
31
  connectors?: {
26
32
  gateways(): Promise<ConnectorsView["gateways"]>;
package/src/http.ts CHANGED
@@ -3,6 +3,8 @@ export class ConsoleHttpError extends Error {
3
3
  constructor(
4
4
  readonly status: number,
5
5
  message: string,
6
+ /** A reason a host opted into showing; appended after the translated message. */
7
+ readonly detail?: string,
6
8
  ) {
7
9
  super(message);
8
10
  }
package/src/skills-api.ts CHANGED
@@ -25,14 +25,13 @@ export async function skillDetail(
25
25
  ): Promise<SkillDetailView> {
26
26
  const entry = skills.catalog().find((s) => s.name === name);
27
27
  if (!entry) throw new HttpError(404, "There is no such skill.");
28
- if (entry.missing) throw new HttpError(404, "The skill file is missing.");
28
+ if (entry.missing)
29
+ throw skills.errorDetail
30
+ ? new HttpError(404, "The skill file is missing: ", entry.missing)
31
+ : new HttpError(404, "The skill file is missing.");
29
32
  try {
33
+ // The host's `read` enforces any size limit; the console shows what it returns.
30
34
  const parsed = await skills.read(name);
31
- if (
32
- Buffer.byteLength(parsed.body) > 1024 * 1024 ||
33
- Buffer.byteLength(JSON.stringify(parsed.frontmatter)) > 64 * 1024
34
- )
35
- throw new HttpError(413, "The skill file is too large.");
36
35
  return {
37
36
  name,
38
37
  body: parsed.body,
@@ -40,7 +39,16 @@ export async function skillDetail(
40
39
  };
41
40
  } catch (error) {
42
41
  if (error instanceof HttpError) throw error;
43
- throw new HttpError(422, "The skill frontmatter could not be read.");
42
+ throw skills.errorDetail
43
+ ? new HttpError(
44
+ 422,
45
+ "The skill frontmatter could not be read: ",
46
+ (error instanceof Error ? error.message : String(error)).slice(
47
+ 0,
48
+ 300,
49
+ ),
50
+ )
51
+ : new HttpError(422, "The skill frontmatter could not be read.");
44
52
  }
45
53
  }
46
54