@treeseed/core 0.5.3 → 0.6.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.
@@ -5,9 +5,9 @@ import {
5
5
  StubExecutionAdapter
6
6
  } from "./agents/adapters/execution.js";
7
7
  import { LocalBranchMutationAdapter } from "./agents/adapters/mutations.js";
8
- import { StubNotificationAdapter } from "./agents/adapters/notification.js";
8
+ import { SdkMessageNotificationAdapter, StubNotificationAdapter } from "./agents/adapters/notification.js";
9
9
  import { GitRepositoryInspectionAdapter, StubRepositoryInspectionAdapter } from "./agents/adapters/repository.js";
10
- import { StubResearchAdapter } from "./agents/adapters/research.js";
10
+ import { ProjectGraphResearchAdapter, StubResearchAdapter } from "./agents/adapters/research.js";
11
11
  import { LocalVerificationAdapter, StubVerificationAdapter } from "./agents/adapters/verification.js";
12
12
  let cachedAgentRuntime = null;
13
13
  function readPluginRecord(pluginEntry, key) {
@@ -44,8 +44,14 @@ function buildAgentRuntime() {
44
44
  ["stub", () => new StubVerificationAdapter()],
45
45
  ["local", () => new LocalVerificationAdapter()]
46
46
  ]);
47
- const notification = /* @__PURE__ */ new Map([["stub", () => new StubNotificationAdapter()]]);
48
- const research = /* @__PURE__ */ new Map([["stub", () => new StubResearchAdapter()]]);
47
+ const notification = /* @__PURE__ */ new Map([
48
+ ["stub", () => new StubNotificationAdapter()],
49
+ ["sdk_message", () => new SdkMessageNotificationAdapter()]
50
+ ]);
51
+ const research = /* @__PURE__ */ new Map([
52
+ ["stub", () => new StubResearchAdapter()],
53
+ ["project_graph", () => new ProjectGraphResearchAdapter()]
54
+ ]);
49
55
  const handlers = /* @__PURE__ */ new Map();
50
56
  for (const pluginEntry of runtime.plugins) {
51
57
  const agentProviders = readPluginRecord(pluginEntry, "agentProviders");
@@ -4,6 +4,19 @@ import { jsonError, requireScope } from "./http.js";
4
4
  async function listRegisteredHandlers() {
5
5
  return listCoreRegisteredAgentHandlers();
6
6
  }
7
+ async function safeListRegisteredHandlers() {
8
+ try {
9
+ return {
10
+ handlers: await listRegisteredHandlers(),
11
+ error: null
12
+ };
13
+ } catch (error) {
14
+ return {
15
+ handlers: [],
16
+ error: error instanceof Error ? error.message : String(error)
17
+ };
18
+ }
19
+ }
7
20
  function withPrefix(prefix, path) {
8
21
  return `${prefix}${path}`.replace(/\/{2,}/g, "/");
9
22
  }
@@ -23,11 +36,15 @@ function authorizeRequest(c, options) {
23
36
  function registerAgentRoutes(app, options) {
24
37
  const prefix = options.prefix ?? "/agent";
25
38
  const defaultActor = options.defaultActor ?? "api";
26
- app.get(withPrefix(prefix, "/healthz"), async (c) => c.json({
27
- ok: true,
28
- service: "treeseed-agent-api",
29
- handlerCount: (await listRegisteredHandlers()).length
30
- }));
39
+ app.get(withPrefix(prefix, "/healthz"), async (c) => {
40
+ const registration = await safeListRegisteredHandlers();
41
+ return c.json({
42
+ ok: true,
43
+ service: "treeseed-agent-api",
44
+ handlerCount: registration.handlers.length,
45
+ registrationError: registration.error
46
+ });
47
+ });
31
48
  app.get(withPrefix(prefix, "/specs"), async (c) => {
32
49
  const unauthorized = authorizeRequest(c, options);
33
50
  if (unauthorized) return unauthorized;
package/dist/content.js CHANGED
@@ -28,6 +28,7 @@ const timeHorizonValues = ["near-term", "mid-term", "long-term"];
28
28
  const runtimeStatusValues = ["active", "experimental", "dormant"];
29
29
  const agentTriggerTypeValues = ["schedule", "message", "follow", "startup"];
30
30
  const agentPermissionOperationValues = ["get", "search", "follow", "pick", "create", "update"];
31
+ const treeseedDocsExtensions = ["markdown", "mdown", "mkdn", "mkd", "mdwn", "md", "mdx"];
31
32
  function hasMarkdownContent(base) {
32
33
  if (!existsSync(base)) {
33
34
  return false;
@@ -39,8 +40,12 @@ function hasMarkdownContent(base) {
39
40
  }
40
41
  return false;
41
42
  }
42
- function optionalMarkdownGlob(base) {
43
- const delegate = glob({ pattern: "**/*.{md,mdx}", base });
43
+ function optionalMarkdownGlob(base, options = {}) {
44
+ const delegate = glob({
45
+ pattern: options.pattern ?? "**/*.{md,mdx}",
46
+ base,
47
+ generateId: options.generateId
48
+ });
44
49
  return {
45
50
  name: `treeseed-optional-markdown-glob:${base}`,
46
51
  async load(context) {
@@ -68,7 +73,10 @@ function resolveDocsCollectionProvider(tenantConfig, dependencies) {
68
73
  const selectedId = pluginRuntime.config.providers.content.docs;
69
74
  if (selectedId === "default") {
70
75
  return {
71
- loader: dependencies.docsLoader({ generateId: createKnowledgeDocId }),
76
+ loader: optionalMarkdownGlob(tenantConfig.content.docs, {
77
+ pattern: `**/[^_]*.{${treeseedDocsExtensions.join(",")}}`,
78
+ generateId: createKnowledgeDocId
79
+ }),
72
80
  schema: dependencies.docsSchema({
73
81
  extend: z.object({
74
82
  tags: z.array(z.string()).default(TREESEED_MODEL_DEFAULTS.tags ?? [])
package/dist/env.yaml CHANGED
@@ -73,31 +73,33 @@ entries:
73
73
  - process-env
74
74
  relevanceRef: railwayManagedEnabled
75
75
  requiredWhenRef: railwayManagedEnabled
76
- RAILWAY_TOKEN:
77
- label: Railway project token
78
- group: auth
79
- description: Optional Railway token for project-scoped resource management after a project already exists.
80
- howToGet: In Railway, generate a project-scoped token only if you need project resource management separate from the primary API token, then paste it here as RAILWAY_TOKEN.
81
- sensitivity: secret
76
+ TREESEED_RAILWAY_WORKSPACE:
77
+ label: Railway workspace
78
+ group: railway
79
+ description: Railway workspace Treeseed should use when listing or creating projects during bootstrap and config reconciliation.
80
+ howToGet: In Railway, use the workspace slug or name shown in the workspace switcher. Treeseed defaults this repository to knowledge-coop unless you override it here.
81
+ sensitivity: plain
82
82
  targets:
83
83
  - local-runtime
84
- - railway-secret
84
+ - railway-var
85
85
  scopes:
86
86
  - local
87
87
  - staging
88
88
  - prod
89
- requirement: optional
89
+ storage: shared
90
+ requirement: conditional
90
91
  purposes:
91
92
  - deploy
92
93
  - destroy
93
94
  - config
94
95
  validation:
95
96
  kind: nonempty
96
- minLength: 8
97
97
  sourcePriority:
98
98
  - machine-config
99
99
  - process-env
100
+ defaultValueRef: railwayWorkspaceDefault
100
101
  relevanceRef: railwayManagedEnabled
102
+ requiredWhenRef: railwayManagedEnabled
101
103
  CLOUDFLARE_ACCOUNT_ID:
102
104
  label: Cloudflare account ID
103
105
  group: cloudflare
@@ -1,8 +1,7 @@
1
1
  import { defineRouteMiddleware } from "../vendor/starlight/route-data.js";
2
2
  import { TREESEED_LINKS, buildStarlightSidebarEntriesFromRuntime, normalizeHref } from "../utils/starlight-nav.js";
3
3
  import { loadHostedBookRuntime } from "../utils/published-content.js";
4
- import { BOOKS, BOOKS_LINK, TREESEED_LIBRARY_DOWNLOAD } from "@treeseed/sdk/platform/books-data";
5
- import { loadTreeseedManifest, tenantModelRendered } from "@treeseed/sdk/platform/tenant-config";
4
+ import { RUNTIME_TENANT } from "../tenant/runtime-config.js";
6
5
  const copyLink = (entry) => ({ ...entry, attrs: { ...entry.attrs } });
7
6
  const copyEntry = (entry) => entry.type === "link" ? copyLink(entry) : {
8
7
  ...entry,
@@ -26,12 +25,28 @@ const setRouteSidebar = (route, currentPath, sidebar, paginationSource) => {
26
25
  route.hasSidebar = sidebar.length > 0;
27
26
  route.pagination = paginationSource ? buildPagination(paginationSource, currentPath) : { prev: void 0, next: void 0 };
28
27
  };
29
- const defaultRuntime = { BOOKS, BOOKS_LINK, TREESEED_LIBRARY_DOWNLOAD, TREESEED_LINKS };
30
- const tenantConfig = loadTreeseedManifest();
28
+ const defaultRuntime = {
29
+ BOOKS: [],
30
+ BOOKS_LINK: {
31
+ label: "Books",
32
+ link: TREESEED_LINKS.home
33
+ },
34
+ TREESEED_LIBRARY_DOWNLOAD: {
35
+ downloadFileName: "treeseed-knowledge.md",
36
+ downloadHref: "/books/treeseed-knowledge.md",
37
+ downloadTitle: "TreeSeed Knowledge Library"
38
+ },
39
+ TREESEED_LINKS
40
+ };
41
+ function runtimeTenantModelRendered(modelName) {
42
+ const featureValue = RUNTIME_TENANT.features?.[modelName];
43
+ const siteValue = RUNTIME_TENANT.site?.[modelName];
44
+ return featureValue ?? siteValue ?? true;
45
+ }
31
46
  const onRequest = defineRouteMiddleware(async (context) => {
32
47
  const route = context.locals.starlightRoute;
33
48
  const currentPath = normalizeHref(context.url.pathname);
34
- if (!tenantModelRendered(tenantConfig, "books")) {
49
+ if (!runtimeTenantModelRendered("books")) {
35
50
  setRouteSidebar(route, currentPath, [], null);
36
51
  return;
37
52
  }
@@ -35,7 +35,7 @@ class RailwayWorkerPoolScaler {
35
35
  fetchImpl;
36
36
  mutation;
37
37
  constructor(options = {}) {
38
- this.apiToken = options.apiToken?.trim() || envValue("RAILWAY_API_TOKEN") || envValue("RAILWAY_TOKEN") || null;
38
+ this.apiToken = options.apiToken?.trim() || envValue("RAILWAY_API_TOKEN") || null;
39
39
  this.apiUrl = options.apiUrl?.trim() || envValue("TREESEED_RAILWAY_API_URL") || DEFAULT_RAILWAY_API_URL;
40
40
  this.serviceId = options.serviceId?.trim() || envValue("TREESEED_RAILWAY_WORKER_SERVICE_ID") || envValue("TREESEED_WORKER_SERVICE_ID") || null;
41
41
  this.environmentId = options.environmentId?.trim() || envValue("TREESEED_RAILWAY_ENVIRONMENT_ID") || null;
@@ -1,29 +1,38 @@
1
- import { readFileSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import { loadTreeseedManifest } from "@treeseed/sdk/platform/tenant-config";
4
1
  import { parseSiteConfig } from "../utils/site-config-schema.js";
5
2
  const injectedTenantConfig = typeof __TREESEED_TENANT_CONFIG__ !== "undefined" ? __TREESEED_TENANT_CONFIG__ : null;
6
3
  const injectedProjectRoot = typeof __TREESEED_PROJECT_ROOT__ !== "undefined" ? __TREESEED_PROJECT_ROOT__ : null;
7
4
  const injectedSiteConfig = typeof __TREESEED_SITE_CONFIG__ !== "undefined" ? __TREESEED_SITE_CONFIG__ : null;
8
- const RUNTIME_PROJECT_ROOT = injectedProjectRoot ?? process.cwd();
5
+ function getNodeBuiltin(name) {
6
+ const getBuiltinModule = globalThis.process?.getBuiltinModule;
7
+ return getBuiltinModule?.(name) ?? null;
8
+ }
9
+ function getCwd() {
10
+ const cwd = globalThis.process?.cwd;
11
+ return cwd?.() ?? ".";
12
+ }
13
+ function resolveRuntimePath(projectRoot, path) {
14
+ const pathModule = getNodeBuiltin("path");
15
+ return pathModule?.resolve(projectRoot, path) ?? `${projectRoot.replace(/\/$/, "")}/${path}`;
16
+ }
17
+ const RUNTIME_PROJECT_ROOT = injectedProjectRoot ?? getCwd();
9
18
  function fallbackTenantConfig(projectRoot) {
10
19
  return {
11
20
  id: "treeseed-runtime",
12
- siteConfigPath: resolve(projectRoot, "treeseed.site.yaml"),
21
+ siteConfigPath: resolveRuntimePath(projectRoot, "treeseed.site.yaml"),
13
22
  content: {
14
- pages: resolve(projectRoot, "src/content/pages"),
15
- notes: resolve(projectRoot, "src/content/notes"),
16
- questions: resolve(projectRoot, "src/content/questions"),
17
- objectives: resolve(projectRoot, "src/content/objectives"),
18
- proposals: resolve(projectRoot, "src/content/proposals"),
19
- decisions: resolve(projectRoot, "src/content/decisions"),
20
- people: resolve(projectRoot, "src/content/people"),
21
- agents: resolve(projectRoot, "src/content/agents"),
22
- books: resolve(projectRoot, "src/content/books"),
23
- docs: resolve(projectRoot, "src/content/knowledge"),
24
- templates: resolve(projectRoot, "src/content/templates"),
25
- knowledge_packs: resolve(projectRoot, "src/content/knowledge-packs"),
26
- workdays: resolve(projectRoot, "src/content/workdays")
23
+ pages: resolveRuntimePath(projectRoot, "src/content/pages"),
24
+ notes: resolveRuntimePath(projectRoot, "src/content/notes"),
25
+ questions: resolveRuntimePath(projectRoot, "src/content/questions"),
26
+ objectives: resolveRuntimePath(projectRoot, "src/content/objectives"),
27
+ proposals: resolveRuntimePath(projectRoot, "src/content/proposals"),
28
+ decisions: resolveRuntimePath(projectRoot, "src/content/decisions"),
29
+ people: resolveRuntimePath(projectRoot, "src/content/people"),
30
+ agents: resolveRuntimePath(projectRoot, "src/content/agents"),
31
+ books: resolveRuntimePath(projectRoot, "src/content/books"),
32
+ docs: resolveRuntimePath(projectRoot, "src/content/knowledge"),
33
+ templates: resolveRuntimePath(projectRoot, "src/content/templates"),
34
+ knowledge_packs: resolveRuntimePath(projectRoot, "src/content/knowledge-packs"),
35
+ workdays: resolveRuntimePath(projectRoot, "src/content/workdays")
27
36
  },
28
37
  features: {
29
38
  docs: true,
@@ -40,15 +49,15 @@ const RUNTIME_TENANT = (() => {
40
49
  if (injectedTenantConfig) {
41
50
  return injectedTenantConfig;
42
51
  }
43
- try {
44
- return loadTreeseedManifest();
45
- } catch {
46
- return fallbackTenantConfig(RUNTIME_PROJECT_ROOT);
47
- }
52
+ return fallbackTenantConfig(RUNTIME_PROJECT_ROOT);
48
53
  })();
49
54
  const RUNTIME_SITE_CONFIG = injectedSiteConfig ?? (() => {
55
+ const fs = getNodeBuiltin("fs");
56
+ if (!fs) {
57
+ return null;
58
+ }
50
59
  try {
51
- return parseSiteConfig(readFileSync(RUNTIME_TENANT.siteConfigPath, "utf8"));
60
+ return parseSiteConfig(fs.readFileSync(RUNTIME_TENANT.siteConfigPath, "utf8"));
52
61
  } catch {
53
62
  return null;
54
63
  }
@@ -1,4 +1,4 @@
1
- import { getTreeseedDeployConfig } from "@treeseed/sdk/platform/plugins";
1
+ import { getTreeseedDeployConfig } from "@treeseed/sdk/platform/deploy-runtime";
2
2
  import {
3
3
  createTeamScopedR2OverlayContentRuntimeProvider,
4
4
  isTeamScopedR2ContentEnabled,
@@ -1,6 +1,6 @@
1
1
  import type { TreeseedContentCollection, TreeseedTenantConfig } from '@treeseed/sdk/platform/contracts';
2
- export declare function isSiteRenderedModel(tenantConfig: Pick<TreeseedTenantConfig, 'features' | 'site'>, modelName: TreeseedContentCollection): boolean;
2
+ export declare function isSiteRenderedModel(tenantConfig: Pick<TreeseedTenantConfig, 'features' | 'site'>, modelName: TreeseedContentCollection): boolean | Partial<Record<TreeseedContentCollection, import("@treeseed/sdk/platform/contracts").TreeseedTenantSiteModelConfig>>;
3
3
  export declare function filterSiteRenderedModels<T extends {
4
4
  model: TreeseedContentCollection;
5
5
  }>(tenantConfig: Pick<TreeseedTenantConfig, 'features' | 'site'>, entries: T[]): T[];
6
- export declare function siteModelRendered(modelName: TreeseedContentCollection): boolean;
6
+ export declare function siteModelRendered(modelName: TreeseedContentCollection): boolean | Partial<Record<TreeseedContentCollection, import("@treeseed/sdk/platform/contracts").TreeseedTenantSiteModelConfig>>;
@@ -1,7 +1,8 @@
1
- import { tenantModelRendered } from "@treeseed/sdk/platform/tenant-config";
2
1
  import { RUNTIME_TENANT } from "../tenant/runtime-config.js";
3
2
  function isSiteRenderedModel(tenantConfig, modelName) {
4
- return tenantModelRendered(tenantConfig, modelName);
3
+ const featureValue = tenantConfig.features?.[modelName];
4
+ const siteValue = tenantConfig.site?.[modelName];
5
+ return featureValue ?? siteValue ?? true;
5
6
  }
6
7
  function filterSiteRenderedModels(tenantConfig, entries) {
7
8
  return entries.filter((entry) => isSiteRenderedModel(tenantConfig, entry.model));
@@ -1,9 +1,3 @@
1
- import {
2
- BOOKS,
3
- BOOKS_LINK,
4
- TREESEED_LIBRARY_DOWNLOAD,
5
- TREESEED_LINKS
6
- } from "@treeseed/sdk/platform/books-data";
7
1
  const normalizeHref = (href) => href.endsWith("/") ? href : `${href}/`;
8
2
  function buildSidebarLink(href, label, currentPath) {
9
3
  return {
@@ -82,6 +76,19 @@ function getDocsDownloadForPathFromRuntime(runtime2, pathname) {
82
76
  downloadTitle: book.downloadTitle
83
77
  };
84
78
  }
79
+ const BOOKS = [];
80
+ const BOOKS_LINK = {
81
+ label: "Books",
82
+ link: "/knowledge/"
83
+ };
84
+ const TREESEED_LIBRARY_DOWNLOAD = {
85
+ downloadFileName: "treeseed-knowledge.md",
86
+ downloadHref: "/books/treeseed-knowledge.md",
87
+ downloadTitle: "TreeSeed Knowledge Library"
88
+ };
89
+ const TREESEED_LINKS = {
90
+ home: "/knowledge/"
91
+ };
85
92
  const runtime = { BOOKS, BOOKS_LINK, TREESEED_LIBRARY_DOWNLOAD, TREESEED_LINKS };
86
93
  function buildBookSidebar(bookSlug) {
87
94
  return buildBookSidebarFromRuntime(runtime, bookSlug);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/core",
3
- "version": "0.5.3",
3
+ "version": "0.6.1",
4
4
  "description": "Treeseed integrated platform starter for Astro/Starlight web runtimes and Hono API runtimes.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -75,7 +75,7 @@
75
75
  "@astrojs/sitemap": "3.7.0",
76
76
  "@astrojs/starlight": "0.37.6",
77
77
  "@tailwindcss/vite": "^4.1.4",
78
- "@treeseed/sdk": "^0.5.3",
78
+ "@treeseed/sdk": "^0.6.0",
79
79
  "astro": "^5.6.1",
80
80
  "esbuild": "^0.28.0",
81
81
  "hono": "^4.8.2",