chapterhouse 0.9.0 → 0.9.1

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/dist/api/korg.js CHANGED
@@ -1,20 +1,3 @@
1
- import { sendToAgentSession } from "../copilot/orchestrator.js";
2
- export async function routeKorgMessage(input) {
3
- const message = input.message.trim();
4
- const sessionId = input.sessionKey?.trim() || `korg-${Date.now()}`;
5
- const sessionName = input.sessionKey?.trim() || message.slice(0, 80).trim() || sessionId;
6
- const prompt = [
7
- "Handle this request as Korg via the API.",
8
- `Research session id: ${sessionId}`,
9
- `Research session name: ${sessionName}`,
10
- `When you ingest sources for this session, pass session_id: \"${sessionId}\" and session_name: \"${sessionName}\" to wiki_ingest_source.`,
11
- "Reply directly to the user with the next best research action or synthesis.",
12
- "",
13
- message,
14
- ].join("\n");
15
- const reply = await sendToAgentSession("korg", prompt);
16
- return { ok: true, sessionKey: sessionId, reply };
17
- }
18
1
  export function listKorgResearchSessions(db) {
19
2
  return db.prepare(`
20
3
  SELECT
@@ -13,8 +13,7 @@
13
13
  // POST /api/wiki/update — static auth guard tested below
14
14
  // POST /api/wiki/page/pin — auth + traversal guard tested
15
15
  // POST /api/wiki/ingest — NOT tested (add in Phase 3c)
16
- // GET /api/wiki/korg/sessions — NOT tested (add in Phase 3c)
17
- // POST /api/wiki/korg — NOT tested (add in Phase 3c)
16
+ // GET /api/wiki/korg/sessions — tested (server.test.ts: grouped active sessions)
18
17
  // ──────────────────────────────────────────────────────────────────────────────
19
18
  import { describe, test } from "node:test";
20
19
  import assert from "node:assert/strict";
@@ -42,7 +42,7 @@ import { recordDecision } from "../memory/decisions.js";
42
42
  import { upsertEntity } from "../memory/entities.js";
43
43
  import { getInboxItem, listPendingInboxItems, resolveInboxItem } from "../memory/inbox.js";
44
44
  import { ingestSource } from "../wiki/ingest.js";
45
- import { listKorgResearchSessions, routeKorgMessage } from "./korg.js";
45
+ import { listKorgResearchSessions } from "./korg.js";
46
46
  const log = childLogger("server");
47
47
  const modeContext = new ModeContext(config);
48
48
  void searchIndex; // re-exported by index-manager; reference here documents the dep
@@ -146,10 +146,6 @@ const prMergeHookSchema = z.object({
146
146
  body: z.string().optional(),
147
147
  files_changed: z.array(z.string()).optional(),
148
148
  });
149
- const korgRequestSchema = z.object({
150
- message: requiredString("Missing 'message' in request body"),
151
- sessionKey: z.string().trim().min(1).optional(),
152
- }).strict();
153
149
  const projectHardRulesSchema = z.object({
154
150
  hardRules: z.object({
155
151
  auto_pr: z.boolean({ error: "hardRules.auto_pr must be a boolean" }),
@@ -209,6 +205,13 @@ function coerceWikiPageUpdated(path, updated) {
209
205
  function wikiPathToBrowserSlug(path) {
210
206
  return path.replace(/^pages\//, "").replace(/\.md$/, "");
211
207
  }
208
+ function entityTypeFromPath(path) {
209
+ const rest = path.startsWith("pages/") ? path.slice("pages/".length) : path;
210
+ const segs = rest.split("/").filter(Boolean);
211
+ if (segs.length <= 1)
212
+ return (segs[0] || "pages").replace(/\.md$/i, "");
213
+ return segs[0];
214
+ }
212
215
  function normalizeOptionalQueryParam(value) {
213
216
  if (typeof value !== "string") {
214
217
  return undefined;
@@ -255,8 +258,8 @@ function listDbWikiBrowserPages(filters) {
255
258
  const clauses = [];
256
259
  const params = [];
257
260
  if (filters.type) {
258
- clauses.push("entity_type = ?");
259
- params.push(filters.type);
261
+ clauses.push("(entity_type = ? OR (entity_type IS NULL AND path LIKE ?))");
262
+ params.push(filters.type, `pages/${filters.type}/%`);
260
263
  }
261
264
  if (filters.q) {
262
265
  clauses.push("(title LIKE ? COLLATE NOCASE OR COALESCE(summary, '') LIKE ? COLLATE NOCASE)");
@@ -272,7 +275,7 @@ function listDbWikiBrowserPages(filters) {
272
275
  slug: wikiPathToBrowserSlug(row.path),
273
276
  title: row.title,
274
277
  summary: row.summary ?? "",
275
- type: row.entity_type ?? "topics",
278
+ type: row.entity_type ?? entityTypeFromPath(row.path),
276
279
  last_updated: coerceWikiPageUpdated(row.path, row.last_updated ?? undefined),
277
280
  ...(row.pinned ? { pinned: true } : {}),
278
281
  }));
@@ -1742,11 +1745,6 @@ app.delete("/api/wiki/page", async (req, res) => {
1742
1745
  const removed = await withWikiWrite(() => deletePage(path));
1743
1746
  res.json({ ok: removed, path });
1744
1747
  });
1745
- app.post("/api/wiki/korg", authMiddleware, async (req, res) => {
1746
- const body = parseRequest(korgRequestSchema, req.body ?? {});
1747
- const result = await routeKorgMessage(body);
1748
- res.json(result);
1749
- });
1750
1748
  app.get("/api/wiki/korg/sessions", authMiddleware, (_req, res) => {
1751
1749
  res.json({ sessions: listKorgResearchSessions(getDb()) });
1752
1750
  });
@@ -314,9 +314,6 @@ export const WikiPageUpdateResponseSchema = z.object({
314
314
  page: WikiPageDetailSchema.shape.page.optional(),
315
315
  pinned: z.boolean().optional(),
316
316
  }).passthrough();
317
- export const WikiKorgResponseSchema = z.object({
318
- reply: z.string(),
319
- }).passthrough();
320
317
  export const WikiReingestResponseSchema = z.object({
321
318
  ok: z.boolean(),
322
319
  }).passthrough();
package/dist/store/db.js CHANGED
@@ -551,6 +551,7 @@ export function getDb() {
551
551
  ensureChapterhouseHome();
552
552
  db = new Database(getDbPath());
553
553
  db.pragma("journal_mode = WAL");
554
+ db.pragma("busy_timeout = 5000");
554
555
  db.pragma("foreign_keys = ON");
555
556
  db.exec(`
556
557
  CREATE TABLE IF NOT EXISTS worker_sessions (
@@ -77,7 +77,7 @@ export function upsertWikiPage(path, frontmatter, summary) {
77
77
  const db = getDb();
78
78
  const normalizedPath = normalizeWikiPath(path);
79
79
  const title = frontmatter.title ?? basenameTitle(normalizedPath);
80
- const entityType = frontmatter.metadata?.["entity_type"] ?? null;
80
+ const entityType = frontmatter.metadata?.["entity_type"] ?? categoryOfPath(normalizedPath);
81
81
  const tags = JSON.stringify(frontmatter.tags ?? []);
82
82
  const lastUpdated = frontmatter.updated ?? new Date().toISOString();
83
83
  db.prepare(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chapterhouse",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Chapterhouse — a team-level AI assistant for engineering teams, built on the GitHub Copilot SDK. Web UI only.",
5
5
  "bin": {
6
6
  "chapterhouse": "dist/cli.js"