@foldrun/core 0.2.0

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.
Files changed (166) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +239 -0
  3. package/dist/index.d.ts +32 -0
  4. package/dist/index.js +43 -0
  5. package/dist/src/agent-tools.d.ts +31 -0
  6. package/dist/src/agent-tools.js +99 -0
  7. package/dist/src/agents-md.d.ts +9 -0
  8. package/dist/src/agents-md.js +83 -0
  9. package/dist/src/api-tools.d.ts +38 -0
  10. package/dist/src/api-tools.js +313 -0
  11. package/dist/src/approvals.d.ts +39 -0
  12. package/dist/src/approvals.js +103 -0
  13. package/dist/src/arrange.d.ts +8 -0
  14. package/dist/src/arrange.js +40 -0
  15. package/dist/src/catalog.d.ts +108 -0
  16. package/dist/src/catalog.js +255 -0
  17. package/dist/src/completions.d.ts +61 -0
  18. package/dist/src/completions.js +578 -0
  19. package/dist/src/confine.d.ts +31 -0
  20. package/dist/src/confine.js +298 -0
  21. package/dist/src/container.d.ts +30 -0
  22. package/dist/src/container.js +172 -0
  23. package/dist/src/context-tools.d.ts +81 -0
  24. package/dist/src/context-tools.js +347 -0
  25. package/dist/src/deploy.d.ts +100 -0
  26. package/dist/src/deploy.js +377 -0
  27. package/dist/src/egress.d.ts +75 -0
  28. package/dist/src/egress.js +133 -0
  29. package/dist/src/evals.d.ts +150 -0
  30. package/dist/src/evals.js +651 -0
  31. package/dist/src/flow-lint.d.ts +16 -0
  32. package/dist/src/flow-lint.js +177 -0
  33. package/dist/src/git-hook.d.ts +1 -0
  34. package/dist/src/git-hook.js +70 -0
  35. package/dist/src/git.d.ts +37 -0
  36. package/dist/src/git.js +101 -0
  37. package/dist/src/gitrepo.d.ts +95 -0
  38. package/dist/src/gitrepo.js +355 -0
  39. package/dist/src/history.d.ts +82 -0
  40. package/dist/src/history.js +259 -0
  41. package/dist/src/kinds.d.ts +95 -0
  42. package/dist/src/kinds.js +421 -0
  43. package/dist/src/library.d.ts +94 -0
  44. package/dist/src/library.js +341 -0
  45. package/dist/src/linkable.d.ts +20 -0
  46. package/dist/src/linkable.js +113 -0
  47. package/dist/src/model-probe.d.ts +21 -0
  48. package/dist/src/model-probe.js +81 -0
  49. package/dist/src/notify.d.ts +61 -0
  50. package/dist/src/notify.js +252 -0
  51. package/dist/src/oauth-presets.d.ts +13 -0
  52. package/dist/src/oauth-presets.js +39 -0
  53. package/dist/src/okf.d.ts +220 -0
  54. package/dist/src/okf.js +656 -0
  55. package/dist/src/openapi.d.ts +66 -0
  56. package/dist/src/openapi.js +342 -0
  57. package/dist/src/paths.d.ts +33 -0
  58. package/dist/src/paths.js +78 -0
  59. package/dist/src/platform.d.ts +32 -0
  60. package/dist/src/platform.js +48 -0
  61. package/dist/src/providers.d.ts +47 -0
  62. package/dist/src/providers.js +114 -0
  63. package/dist/src/refs.d.ts +20 -0
  64. package/dist/src/refs.js +78 -0
  65. package/dist/src/run-container.d.ts +227 -0
  66. package/dist/src/run-container.js +760 -0
  67. package/dist/src/runner.d.ts +241 -0
  68. package/dist/src/runner.js +3215 -0
  69. package/dist/src/runtime.d.ts +41 -0
  70. package/dist/src/runtime.js +357 -0
  71. package/dist/src/script-tools.d.ts +62 -0
  72. package/dist/src/script-tools.js +296 -0
  73. package/dist/src/secret-files.d.ts +12 -0
  74. package/dist/src/secret-files.js +92 -0
  75. package/dist/src/secrets.d.ts +147 -0
  76. package/dist/src/secrets.js +626 -0
  77. package/dist/src/starter.d.ts +21 -0
  78. package/dist/src/starter.js +399 -0
  79. package/dist/src/step-exec.d.ts +136 -0
  80. package/dist/src/step-exec.js +391 -0
  81. package/dist/src/storage.d.ts +123 -0
  82. package/dist/src/storage.js +640 -0
  83. package/dist/src/store.d.ts +1052 -0
  84. package/dist/src/store.js +2378 -0
  85. package/dist/src/tar.d.ts +30 -0
  86. package/dist/src/tar.js +111 -0
  87. package/dist/src/tool-names.d.ts +39 -0
  88. package/dist/src/tool-names.js +79 -0
  89. package/dist/src/tool-programs.d.ts +18 -0
  90. package/dist/src/tool-programs.js +52 -0
  91. package/dist/src/tool-test.d.ts +19 -0
  92. package/dist/src/tool-test.js +335 -0
  93. package/dist/src/translator.d.ts +159 -0
  94. package/dist/src/translator.js +986 -0
  95. package/dist/src/triggers.d.ts +60 -0
  96. package/dist/src/triggers.js +270 -0
  97. package/dist/src/webhook.d.ts +47 -0
  98. package/dist/src/webhook.js +148 -0
  99. package/index.ts +44 -0
  100. package/package.json +64 -0
  101. package/src/agent-tools.ts +138 -0
  102. package/src/agents-md.ts +81 -0
  103. package/src/api-tools.ts +389 -0
  104. package/src/approvals.ts +129 -0
  105. package/src/arrange.ts +42 -0
  106. package/src/catalog.ts +321 -0
  107. package/src/completions.ts +652 -0
  108. package/src/confine.ts +350 -0
  109. package/src/container.ts +220 -0
  110. package/src/context-tools.ts +435 -0
  111. package/src/deploy.ts +458 -0
  112. package/src/egress.ts +176 -0
  113. package/src/evals.ts +812 -0
  114. package/src/flow-lint.ts +215 -0
  115. package/src/git-hook.ts +73 -0
  116. package/src/git.ts +117 -0
  117. package/src/gitrepo.ts +391 -0
  118. package/src/history.ts +329 -0
  119. package/src/kinds.ts +507 -0
  120. package/src/library.ts +419 -0
  121. package/src/linkable.ts +123 -0
  122. package/src/model-probe.ts +112 -0
  123. package/src/notify.ts +281 -0
  124. package/src/oauth-presets.ts +54 -0
  125. package/src/okf.ts +768 -0
  126. package/src/openapi.ts +383 -0
  127. package/src/paths.ts +79 -0
  128. package/src/platform.ts +87 -0
  129. package/src/providers.ts +147 -0
  130. package/src/refs.ts +83 -0
  131. package/src/run-container.ts +883 -0
  132. package/src/runner.ts +3446 -0
  133. package/src/runtime.ts +404 -0
  134. package/src/script-tools.ts +383 -0
  135. package/src/secret-files.ts +101 -0
  136. package/src/secrets.ts +799 -0
  137. package/src/starter.ts +415 -0
  138. package/src/step-exec.ts +498 -0
  139. package/src/storage.ts +771 -0
  140. package/src/store.ts +2985 -0
  141. package/src/tar.ts +124 -0
  142. package/src/tool-names.ts +91 -0
  143. package/src/tool-programs.ts +65 -0
  144. package/src/tool-test.ts +401 -0
  145. package/src/translator.ts +1029 -0
  146. package/src/triggers.ts +287 -0
  147. package/src/webhook.ts +183 -0
  148. package/templates/blog-desk/AGENTS.md +13 -0
  149. package/templates/blog-desk/agents/researcher/agent.md +11 -0
  150. package/templates/blog-desk/agents/writer/agent.md +8 -0
  151. package/templates/blog-desk/evals/writer-quality.md +10 -0
  152. package/templates/blog-desk/flows/publish.md +10 -0
  153. package/templates/blog-desk/knowledge/house-style.md +9 -0
  154. package/templates/blog-desk/knowledge/index.md +9 -0
  155. package/templates/hello/AGENTS.md +18 -0
  156. package/templates/hello/agents/curator/agent.md +32 -0
  157. package/templates/hello/agents/notetaker/agent.md +24 -0
  158. package/templates/hello/evals/house-style.md +16 -0
  159. package/templates/hello/flows/memory-review.md +7 -0
  160. package/templates/hello/flows/note.md +7 -0
  161. package/templates/hello/knowledge/house-style.md +15 -0
  162. package/templates/hello/knowledge/index.md +9 -0
  163. package/templates/hello/skills/plain-english/SKILL.md +11 -0
  164. package/templates/hello/state/publishing.md +7 -0
  165. package/templates/hello/tools/wordcount/run.py +13 -0
  166. package/templates/hello/tools/wordcount/tool.md +22 -0
@@ -0,0 +1,259 @@
1
+ // Every change to a workspace, kept.
2
+ //
3
+ // A workspace is a few hundred kilobytes of markdown, and until now the only
4
+ // record of a change was the file after it. A save from the dashboard, a
5
+ // deploy from git, an agent writing memory — each overwrote what was there
6
+ // and nothing remembered the before. Two things were impossible as a result:
7
+ // answering "what did this look like yesterday", and attributing a change in
8
+ // an eval score to the change in a file that caused it.
9
+ //
10
+ // So every write records a REVISION: what changed, from what, to what, by
11
+ // whom, and why. A deploy from git records one revision for the whole push
12
+ // with the commit as its id; a dashboard save records one for the file with
13
+ // a local id. Observe attributes eval scores to revision ids, so the "this
14
+ // change made it worse" table works for a workspace that has never seen a
15
+ // git repository — and when one is connected later, its commits slot into
16
+ // the same series.
17
+ //
18
+ // No git binary, no working copy, no repository on the server. The platform
19
+ // image deliberately has none (deploy.ts explains why); this is a journal of
20
+ // content, which is all the questions above need. Connecting a repository
21
+ // is a separate step and pushes THIS history out, not the other way round.
22
+ //
23
+ // Storage: <data>/<tenant>/.history/<scope>/<id>.json, one file per revision,
24
+ // plus index.jsonl — one line each, newest last — so listing does not open
25
+ // every revision. Content is stored inline: the files are small, and a
26
+ // content-addressed store would be machinery in search of a problem.
27
+ import fs from "node:fs";
28
+ import path from "node:path";
29
+ import crypto from "node:crypto";
30
+ import { dataRoot } from "./paths.js";
31
+ import { commitChanges, gitAvailable, headSha, listCommits, readCommit, repoExists, } from "./gitrepo.js";
32
+ let readTree = () => null;
33
+ export function registerTreeReader(fn) {
34
+ const prev = readTree;
35
+ readTree = (tenant, scope) => fn(tenant, scope) ?? prev(tenant, scope);
36
+ }
37
+ const afterCommit = [];
38
+ export function registerAfterCommit(fn) {
39
+ afterCommit.push(fn);
40
+ }
41
+ export function notifyCommit(tenant, scope, sha) {
42
+ for (const fn of afterCommit) {
43
+ try {
44
+ fn(tenant, scope, sha);
45
+ }
46
+ catch {
47
+ // a mirror that fails must not fail the save that triggered it
48
+ }
49
+ }
50
+ }
51
+ /** Journal or git? Git whenever the binary exists; the journal is the
52
+ * fallback for an install without one. An install that started on the
53
+ * journal keeps it readable — both are consulted on read. */
54
+ const useGit = () => process.env.FOLDRUN_HISTORY !== "journal" && gitAvailable();
55
+ // Not store's assertSafeName: store imports this module, and a cycle between
56
+ // the two is avoidable for one regex.
57
+ function assertSafeName(v, what) {
58
+ if (!/^[A-Za-z0-9][A-Za-z0-9._@-]{0,120}$/.test(v))
59
+ throw new Error(`illegal ${what}: ${v}`);
60
+ }
61
+ /** Anything over this is not a document and is recorded by name only. */
62
+ const MAX_INLINE = 256 * 1024;
63
+ const historyDir = (tenant, scope) => {
64
+ assertSafeName(tenant, "tenant");
65
+ // "@library" for the account shelf; a workspace name otherwise.
66
+ if (scope !== "@library")
67
+ assertSafeName(scope, "workspace");
68
+ return path.join(dataRoot(), tenant, ".history", scope);
69
+ };
70
+ /** A local revision id: sortable by time, unique enough, visibly not a sha. */
71
+ export function newRevisionId(now = new Date()) {
72
+ return `r-${now.getTime().toString(36)}-${crypto.randomBytes(3).toString("hex")}`;
73
+ }
74
+ /**
75
+ * Record one change set. Called by the writers, never by hand.
76
+ *
77
+ * Returns null and records nothing when nothing actually changed — a save
78
+ * of identical content is not a revision, and a history full of "no change"
79
+ * entries is a history nobody reads.
80
+ */
81
+ export function recordRevision(tenant, scope, files, meta = {}) {
82
+ const changed = files.filter((f) => f.before !== f.after);
83
+ if (changed.length === 0)
84
+ return null;
85
+ if (useGit()) {
86
+ // First commit ever for this scope: import the whole tree as it stands
87
+ // now (the writer has already written), so the repository is complete
88
+ // from its first commit rather than starting with one file.
89
+ const first = !headSha(tenant, scope);
90
+ // Generated bundle files (knowledge/index.md, memory/log.md) are rewritten
91
+ // by the same save that changed a concept, outside this call. Commit them
92
+ // as they now stand, or a clone's index disagrees with its own files —
93
+ // the exact drift the template tests exist to catch.
94
+ const generated = first
95
+ ? []
96
+ : (readTree(tenant, scope) ?? []).filter((f) => /(^|\/)(knowledge|memory)\/(.*\/)?(index|log)\.md$/.test(f.path));
97
+ const toCommit = first ? (readTree(tenant, scope) ?? changed) : [...changed, ...generated];
98
+ const sha = commitChanges(tenant, scope, toCommit, {
99
+ by: meta.by,
100
+ // Named after what the caller changed, not the generated files that
101
+ // rode along — "edited knowledge/style.md", not "changed 3 files".
102
+ message: first ? `initial import${meta.message ? ` — ${meta.message}` : ""}` : (meta.message ?? describe(changed)),
103
+ });
104
+ if (!sha)
105
+ return null;
106
+ notifyCommit(tenant, scope, sha);
107
+ return {
108
+ id: meta.commit ?? sha,
109
+ at: new Date().toISOString(),
110
+ by: meta.by ?? "system",
111
+ message: meta.message ?? describe(changed),
112
+ commit: sha,
113
+ files: changed,
114
+ };
115
+ }
116
+ const dir = historyDir(tenant, scope);
117
+ fs.mkdirSync(dir, { recursive: true });
118
+ const rev = {
119
+ id: meta.id ?? meta.commit ?? newRevisionId(),
120
+ at: new Date().toISOString(),
121
+ by: meta.by ?? "system",
122
+ message: meta.message ?? describe(changed),
123
+ commit: meta.commit ?? null,
124
+ files: changed.map((f) => ({
125
+ path: f.path,
126
+ before: clip(f.before),
127
+ after: clip(f.after),
128
+ })),
129
+ };
130
+ assertSafeName(rev.id, "revision id");
131
+ fs.writeFileSync(path.join(dir, `${rev.id}.json`), JSON.stringify(rev));
132
+ const line = {
133
+ id: rev.id,
134
+ at: rev.at,
135
+ by: rev.by,
136
+ message: rev.message,
137
+ commit: rev.commit,
138
+ paths: rev.files.map((f) => f.path),
139
+ };
140
+ fs.appendFileSync(path.join(dir, "index.jsonl"), `${JSON.stringify(line)}\n`);
141
+ return rev;
142
+ }
143
+ const clip = (s) => s === null ? null : s.length > MAX_INLINE ? `[${s.length} bytes — too large to keep inline]` : s;
144
+ /** "edited tools/x/run.py", "created 3 files", "deleted flows/old.md". */
145
+ function describe(files) {
146
+ if (files.length === 1) {
147
+ const f = files[0];
148
+ const verb = f.before === null ? "created" : f.after === null ? "deleted" : "edited";
149
+ return `${verb} ${f.path}`;
150
+ }
151
+ const created = files.filter((f) => f.before === null).length;
152
+ const deleted = files.filter((f) => f.after === null).length;
153
+ const edited = files.length - created - deleted;
154
+ return [edited && `edited ${edited}`, created && `created ${created}`, deleted && `deleted ${deleted}`]
155
+ .filter(Boolean)
156
+ .join(", ") + ` file${files.length === 1 ? "" : "s"}`;
157
+ }
158
+ /** Newest first. `forPath` narrows to revisions that touched one file. */
159
+ export function listRevisions(tenant, scope, opts = {}) {
160
+ if (useGit() && repoExists(tenant, scope)) {
161
+ const fromGit = listCommits(tenant, scope, opts);
162
+ // Anything the journal holds from before the repository existed comes
163
+ // after git's rows: older, and still worth showing.
164
+ return [...fromGit, ...journalRevisions(tenant, scope, opts)].slice(0, opts.limit ?? 200);
165
+ }
166
+ return journalRevisions(tenant, scope, opts);
167
+ }
168
+ function journalRevisions(tenant, scope, opts = {}) {
169
+ const file = path.join(historyDir(tenant, scope), "index.jsonl");
170
+ if (!fs.existsSync(file))
171
+ return [];
172
+ const all = fs
173
+ .readFileSync(file, "utf8")
174
+ .split("\n")
175
+ .filter(Boolean)
176
+ .flatMap((l) => {
177
+ try {
178
+ return [JSON.parse(l)];
179
+ }
180
+ catch {
181
+ return []; // a torn line from a crash mid-append loses one entry, not the file
182
+ }
183
+ })
184
+ .reverse();
185
+ const narrowed = opts.forPath ? all.filter((r) => r.paths.includes(opts.forPath)) : all;
186
+ return narrowed.slice(0, opts.limit ?? 200);
187
+ }
188
+ export function readRevision(tenant, scope, id, forPath) {
189
+ assertSafeName(id, "revision id");
190
+ if (useGit() && repoExists(tenant, scope) && /^[0-9a-f]{7,40}$/.test(id)) {
191
+ const fromGit = readCommit(tenant, scope, id, forPath);
192
+ if (fromGit)
193
+ return fromGit;
194
+ }
195
+ const file = path.join(historyDir(tenant, scope), `${id}.json`);
196
+ if (!fs.existsSync(file))
197
+ return null;
198
+ return JSON.parse(fs.readFileSync(file, "utf8"));
199
+ }
200
+ /**
201
+ * Make sure a scope's repository has its initial import, so a clone made
202
+ * right now is complete. A no-op once any commit exists.
203
+ */
204
+ export function ensureImported(tenant, scope) {
205
+ if (!useGit())
206
+ return null;
207
+ if (headSha(tenant, scope))
208
+ return headSha(tenant, scope);
209
+ const tree = readTree(tenant, scope);
210
+ if (!tree || tree.length === 0)
211
+ return null;
212
+ return commitChanges(tenant, scope, tree, { by: "foldrun", message: "initial import" });
213
+ }
214
+ /** The most recent revision id for a scope — what a change is attributed
215
+ * to when nothing more specific (a deployed commit) is on record. */
216
+ export function latestRevisionId(tenant, scope) {
217
+ return listRevisions(tenant, scope, { limit: 1 })[0]?.id ?? null;
218
+ }
219
+ /**
220
+ * A line diff, for showing a revision. Plain LCS — quadratic, and fine: the
221
+ * files are documents, not generated code, and a diff of two 300-line files
222
+ * is ninety thousand cells, which is nothing.
223
+ */
224
+ export function diffLines(before, after) {
225
+ const a = before.split("\n");
226
+ const b = after.split("\n");
227
+ const n = a.length;
228
+ const m = b.length;
229
+ // lcs[i][j] = length of the LCS of a[i..] and b[j..]
230
+ const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
231
+ for (let i = n - 1; i >= 0; i--) {
232
+ for (let j = m - 1; j >= 0; j--) {
233
+ lcs[i][j] = a[i] === b[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
234
+ }
235
+ }
236
+ const out = [];
237
+ let i = 0;
238
+ let j = 0;
239
+ while (i < n && j < m) {
240
+ if (a[i] === b[j]) {
241
+ out.push({ kind: "same", text: a[i] });
242
+ i++;
243
+ j++;
244
+ }
245
+ else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
246
+ out.push({ kind: "del", text: a[i] });
247
+ i++;
248
+ }
249
+ else {
250
+ out.push({ kind: "add", text: b[j] });
251
+ j++;
252
+ }
253
+ }
254
+ while (i < n)
255
+ out.push({ kind: "del", text: a[i++] });
256
+ while (j < m)
257
+ out.push({ kind: "add", text: b[j++] });
258
+ return out;
259
+ }
@@ -0,0 +1,95 @@
1
+ /** Where a document can live. Nearest wins at read time. */
2
+ export type Scope = "account" | "workspace" | "agent";
3
+ export type Kind = "agents" | "flows" | "evals" | "tools" | "skills" | "memory" | "knowledge" | "scripts";
4
+ export interface KindMeta {
5
+ kind: Kind;
6
+ /** Singular noun, for buttons and prose. */
7
+ one: string;
8
+ /**
9
+ * The OKF concept type a new document of this kind starts with, for the two
10
+ * kinds that are OKF bundles. Null everywhere else: a kind is read from the
11
+ * path, so nothing else declares what it is.
12
+ *
13
+ * Only a starting point even where it is set — the vocabulary is open.
14
+ */
15
+ docType: string | null;
16
+ /** The frontmatter key carrying `docType`. Always OKF's `type`, or null. */
17
+ docKey: "type" | null;
18
+ /** Scopes this kind can live at, nearest last. */
19
+ scopes: Scope[];
20
+ /** Shown in the name box. */
21
+ placeholder: string;
22
+ /** One line under the name box saying what you're about to make. */
23
+ hint: string;
24
+ /** Path relative to the scope root. */
25
+ file: (name: string) => string;
26
+ /** Starting content. */
27
+ template: (name: string, ctx?: TemplateContext) => string;
28
+ }
29
+ /** What the caller knows that the template can use. Only flows need it — a new
30
+ * flow that names a real agent is one edit closer to running than one that
31
+ * names a placeholder. */
32
+ export interface TemplateContext {
33
+ firstAgent?: string;
34
+ }
35
+ export declare const KINDS: Record<Kind, KindMeta>;
36
+ /**
37
+ * What a script tool can be written in.
38
+ *
39
+ * Three, not every language the runner can execute. The runner picks an
40
+ * interpreter from the file extension, so `.rb` and the rest keep working the
41
+ * moment someone renames a file — but a picker with nine entries is a quiz,
42
+ * and three covers what people actually reach for. The point of asking at all
43
+ * is that "script tool" silently meaning JavaScript was a thing you had to
44
+ * find out by reading the file it made.
45
+ */
46
+ export declare const SCRIPT_LANGUAGES: readonly [{
47
+ readonly value: "javascript";
48
+ readonly label: "JavaScript";
49
+ readonly ext: ".mjs";
50
+ readonly hint: "node — the default";
51
+ }, {
52
+ readonly value: "python";
53
+ readonly label: "Python";
54
+ readonly ext: ".py";
55
+ readonly hint: "python3";
56
+ }, {
57
+ readonly value: "bash";
58
+ readonly label: "Shell";
59
+ readonly ext: ".sh";
60
+ readonly hint: "bash — for wrapping a command";
61
+ }];
62
+ export type ScriptLanguage = (typeof SCRIPT_LANGUAGES)[number]["value"];
63
+ /**
64
+ * A new tool, as the files it starts as.
65
+ *
66
+ * Three transports, two shapes. An API or an MCP server is a definition and
67
+ * nothing else, so it stays one flat file. A script tool is a definition and
68
+ * the code it runs, which cannot be separated without the tool silently
69
+ * resolving to nothing — so it starts as a folder holding both, with `run:`
70
+ * relative to itself and naming no scope.
71
+ *
72
+ * Returned as a list because "create a tool" is not always one write, and
73
+ * the callers that stamp templates should not have to know which case they
74
+ * are in.
75
+ */
76
+ export declare function toolStarter(name: string, transport: "http" | "script" | "mcp", language?: ScriptLanguage): {
77
+ file: string;
78
+ content: string;
79
+ }[];
80
+ export declare const ALL_KINDS: Kind[];
81
+ /** The kinds that exist at a given scope. */
82
+ export declare const kindsAt: (scope: Scope) => Kind[];
83
+ /** The noun a kind declares, for asserting a file says what it is. */
84
+ export declare const docTypeOf: (kind: Kind) => string | null;
85
+ /** Which frontmatter key carries that noun — `kind:` for ours, `type:` for OKF's. */
86
+ export declare const docKeyOf: (kind: Kind) => "type" | null;
87
+ /**
88
+ * How a tool reaches the thing it calls.
89
+ *
90
+ * Read from `transport:`, falling back to a legacy `type: http|script|mcp`
91
+ * written before `type:` meant the document. Files written either way keep
92
+ * working; only one of them is what we generate now.
93
+ */
94
+ export type Transport = "http" | "script" | "mcp";
95
+ export declare function readTransport(data: Record<string, unknown>): Transport | null;