wizards 0.2.17 → 0.2.18

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 (65) hide show
  1. package/apps/runtime/dist/agents/project-assistant.js +20 -2
  2. package/apps/runtime/dist/authoring/guide.js +6 -3
  3. package/apps/runtime/dist/db/migrations/tenant/0009_knowledge.sql +104 -0
  4. package/apps/runtime/dist/db/migrations/tenant/meta/0009_snapshot.json +2837 -0
  5. package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +7 -0
  6. package/apps/runtime/dist/db/schema.js +160 -8
  7. package/apps/runtime/dist/engine/steps.js +3 -11
  8. package/apps/runtime/dist/env.js +2 -0
  9. package/apps/runtime/dist/files/storage.js +15 -0
  10. package/apps/runtime/dist/models.js +38 -0
  11. package/apps/runtime/dist/plugins/loader.js +2 -0
  12. package/apps/runtime/dist/routes/studio.js +147 -6
  13. package/apps/runtime/dist/services/cloud.js +59 -1
  14. package/apps/runtime/dist/services/knowledge-model.js +558 -0
  15. package/apps/runtime/dist/services/knowledge.js +721 -0
  16. package/apps/runtime/dist/services/project-files.js +114 -6
  17. package/apps/runtime/dist/services/project-index.js +632 -94
  18. package/apps/runtime/dist/services/relevance.js +90 -0
  19. package/apps/runtime/dist/services/space-categories.js +882 -0
  20. package/apps/runtime/dist/services/space-data.js +466 -38
  21. package/apps/runtime/dist/services/space-origin.js +164 -0
  22. package/apps/runtime/dist/services/spaces.js +155 -1
  23. package/apps/runtime/dist/tools/pages.js +27 -22
  24. package/apps/runtime/dist/tools/project.js +130 -40
  25. package/apps/web/dist/assets/{Account-LVXhagaS.js → Account-Cp1cgqjV.js} +1 -1
  26. package/apps/web/dist/assets/{ChangelogDialog-DM0KAoy6.js → ChangelogDialog-Chke1qUe.js} +1 -1
  27. package/apps/web/dist/assets/{ChatPanel-CxOQmtv-.js → ChatPanel-DWrwewr6.js} +1 -1
  28. package/apps/web/dist/assets/{CreditsDialog-DkW3YT44.js → CreditsDialog-Ckema8Ss.js} +1 -1
  29. package/apps/web/dist/assets/{EditorPage-hbisE1Wf.js → EditorPage-XX6pfHk5.js} +1 -1
  30. package/apps/web/dist/assets/{FlowPreview-v7or29ku.js → FlowPreview-DS545n78.js} +1 -1
  31. package/apps/web/dist/assets/{Harness-RZF-bBmb.js → Harness-H3yXP-2L.js} +1 -1
  32. package/apps/web/dist/assets/{HomePage-mxG-a_KU.js → HomePage-Bds84ShK.js} +1 -1
  33. package/apps/web/dist/assets/{ImportWizard-CiPyUt3E.js → ImportWizard-0_NkA8aS.js} +1 -1
  34. package/apps/web/dist/assets/{LocalRuntime-Dqp9wkN3.js → LocalRuntime-DnABzTOe.js} +1 -1
  35. package/apps/web/dist/assets/{NewWizardPage-DHwRsi0_.js → NewWizardPage-BqP75V9K.js} +2 -2
  36. package/apps/web/dist/assets/ProjectPage-DbOvwOdZ.js +5 -0
  37. package/apps/web/dist/assets/{PublicRunner-TGkOwbAP.js → PublicRunner-CtZ7vxl_.js} +1 -1
  38. package/apps/web/dist/assets/{RunnerView-DzHGshZj.js → RunnerView-xV5bWclu.js} +1 -1
  39. package/apps/web/dist/assets/{SettingsPage-CG3Xtfej.js → SettingsPage-DKt55zFh.js} +1 -1
  40. package/apps/web/dist/assets/{SetupPage-O1GfPlqX.js → SetupPage-c6mMD9zN.js} +1 -1
  41. package/apps/web/dist/assets/{SharePage-CCNUhtry.js → SharePage-DTKIbMj-.js} +1 -1
  42. package/apps/web/dist/assets/{api-DB22_B7G.js → api-CVG-xDEn.js} +1 -1
  43. package/apps/web/dist/assets/changelog-CXXT0zsp.js +1 -0
  44. package/apps/web/dist/assets/{esm-3TT9i_rv.js → esm-DLM3jdbm.js} +1 -1
  45. package/apps/web/dist/assets/{index-CLe9Wrpu.js → index-1gmaxDSk.js} +3 -3
  46. package/apps/web/dist/assets/index-Bwyehu0k.css +1 -0
  47. package/apps/web/dist/assets/{meta-CEsBCPfA.js → meta-m947x6uG.js} +1 -1
  48. package/apps/web/dist/assets/{settings-menu-pOcjznGJ.js → settings-menu-DUY6__C3.js} +1 -1
  49. package/apps/web/dist/assets/ui-BPY2pIh0.js +1 -0
  50. package/apps/web/dist/index.html +4 -4
  51. package/apps/web/dist/mcp/flow.html +2 -2
  52. package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +164 -0
  53. package/node_modules/@engenty-wizards/shared/dist/knowledge.d.ts +199 -0
  54. package/node_modules/@engenty-wizards/shared/dist/knowledge.js +68 -0
  55. package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +1 -16
  56. package/node_modules/@engenty-wizards/shared/dist/projects.js +1 -1
  57. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +8 -0
  58. package/node_modules/@engenty-wizards/shared/dist/run.js +5 -0
  59. package/node_modules/@engenty-wizards/shared/dist/space-data.d.ts +20 -0
  60. package/node_modules/@engenty-wizards/shared/dist/space-data.js +2 -1
  61. package/package.json +1 -1
  62. package/apps/web/dist/assets/ProjectPage-Bb7jN_6n.js +0 -4
  63. package/apps/web/dist/assets/changelog-BkIJdZ7M.js +0 -1
  64. package/apps/web/dist/assets/index-BPpKz65x.css +0 -1
  65. package/apps/web/dist/assets/ui-I7QYMX0F.js +0 -1
@@ -188,6 +188,168 @@ export interface PluginIndex {
188
188
  /** Takes one key out, or without a key every entry of the plugin in the space. */
189
189
  remove(space: string, key?: string): Promise<void>;
190
190
  }
191
+ /** A Kategorie of a space: a typed property set on pages, tables, rows and files of Wissen. */
192
+ export interface PluginCategory {
193
+ name: string;
194
+ type: "choice" | "date" | "number" | "text" | "boolean";
195
+ /** A number's unit: `h`, `€`, `kg`. */
196
+ unit?: string;
197
+ /** A choice: several values per item. */
198
+ multiple?: boolean;
199
+ /** A choice whose values are ranked, in the order of `values`. */
200
+ ordered?: boolean;
201
+ /** Where its values come from, in a few words: "Kopfzeile", "Filter der Website". */
202
+ hint?: string;
203
+ /** A choice's values, known beforehand: a site's filter. Others are added as items bring them. */
204
+ values?: string[];
205
+ /** A column of a table the plugin wrote (by the table's key) whose cells are its values. */
206
+ column?: {
207
+ table: string;
208
+ column: string;
209
+ };
210
+ }
211
+ /**
212
+ * Kategorien of an item, by their names: a choice's value (or a list of them), a day as
213
+ * `YYYY-MM-DD`, a number, a short text, true or false. Null takes one off.
214
+ */
215
+ export type PluginCategoryValues = Record<string, string | number | boolean | (string | number)[] | null>;
216
+ /** The original an item was read from, kept as the evidence beside it. */
217
+ export interface PluginOriginal {
218
+ name: string;
219
+ mime: string;
220
+ data: Uint8Array;
221
+ }
222
+ /** `written`: the plugin's version stands. `kept`: a person changed it, the plugin no longer writes it. */
223
+ export type PluginItemState = "written" | "kept";
224
+ export interface PluginPageInput {
225
+ space: string;
226
+ /** The plugin's own name for the page: the same key again rewrites it, never doubles it. */
227
+ key: string;
228
+ /** What the person knows its source as: "Tagesberichte". */
229
+ label?: string;
230
+ title: string;
231
+ markdown: string;
232
+ /** The key of a page the plugin wrote before, to stand under it as a sub-page. */
233
+ parent?: string;
234
+ position?: number;
235
+ original?: PluginOriginal;
236
+ categories?: PluginCategoryValues;
237
+ /** A model fills the space's Kategorien the page did not get from `categories`. */
238
+ fill?: boolean;
239
+ /** Why a person should look at it ("hardly readable"); shown in Wissen. */
240
+ review?: string | null;
241
+ }
242
+ export interface PluginTableInput {
243
+ space: string;
244
+ key: string;
245
+ label?: string;
246
+ title: string;
247
+ /** Typed columns, as a table of the space has them (`text`, `number`, `date`, `select` …). */
248
+ columns: unknown[];
249
+ /** Cells by column id. They replace the rows the key had. */
250
+ rows: Record<string, unknown>[];
251
+ /** `faq`: a question and its answer per row. */
252
+ format?: "faq";
253
+ original?: PluginOriginal;
254
+ categories?: PluginCategoryValues;
255
+ review?: string | null;
256
+ }
257
+ export interface PluginFileInput {
258
+ space: string;
259
+ key: string;
260
+ label?: string;
261
+ name: string;
262
+ mime: string;
263
+ data: Uint8Array;
264
+ /** What it is, in a sentence: a file that is no page is found by its name and this. */
265
+ description?: string;
266
+ /** What a step reads of it, when there is text. */
267
+ text?: string;
268
+ categories?: PluginCategoryValues;
269
+ }
270
+ export interface PluginDocumentInput {
271
+ space: string;
272
+ key: string;
273
+ label?: string;
274
+ name: string;
275
+ mime: string;
276
+ data: Uint8Array;
277
+ /** Set on what it becomes: its first page, its first table, or the file. */
278
+ categories?: PluginCategoryValues;
279
+ /** A model fills the space's Kategorien it did not get from `categories`. */
280
+ fill?: boolean;
281
+ }
282
+ /** What a document became: pages and tables (the first is the one it is found as), or a file. */
283
+ export interface PluginDocumentResult {
284
+ /** The file, kept as the original. */
285
+ id: string;
286
+ path: string;
287
+ state: PluginItemState;
288
+ items: {
289
+ kind: "page" | "table";
290
+ id: string;
291
+ path: string;
292
+ title: string;
293
+ }[];
294
+ /** Why a person should look at it: hardly readable, or not readable at all. */
295
+ review: string | null;
296
+ }
297
+ /** What a plugin wrote into a space. */
298
+ export interface PluginSpaceItem {
299
+ key: string;
300
+ kind: "page" | "table" | "file";
301
+ id: string;
302
+ title: string;
303
+ /** How a step names it: `pages/…`, `tables/…`, `files/…`. */
304
+ path: string;
305
+ state: PluginItemState;
306
+ review: string | null;
307
+ updatedAt: string;
308
+ }
309
+ /**
310
+ * Wissen of a space, written by a plugin: pages, tables and files under keys of its own, with
311
+ * their Kategorien. Every write is indexed: `project_search` finds it a moment later.
312
+ */
313
+ export interface PluginSpaceData {
314
+ /**
315
+ * Adds Kategorien to the space, or values to ones it has. `proposed`: shown to the person to
316
+ * confirm, never filled before; a later call without it takes them.
317
+ */
318
+ putCategories(input: {
319
+ space: string;
320
+ categories: PluginCategory[];
321
+ proposed?: boolean;
322
+ }): Promise<void>;
323
+ putPage(input: PluginPageInput): Promise<{
324
+ id: string;
325
+ path: string;
326
+ state: PluginItemState;
327
+ }>;
328
+ putTable(input: PluginTableInput): Promise<{
329
+ id: string;
330
+ path: string;
331
+ state: PluginItemState;
332
+ }>;
333
+ putFile(input: PluginFileInput): Promise<{
334
+ id: string;
335
+ path: string;
336
+ }>;
337
+ /**
338
+ * A document read as an upload is: a page (sub-pages when it is long and has headings of its
339
+ * own), a table per sheet of a workbook, or a file when nothing reads it. The document stays as
340
+ * the original. The same key again reads it again, unless a person changed what it became.
341
+ */
342
+ putDocument(input: PluginDocumentInput): Promise<PluginDocumentResult>;
343
+ /** Everything the plugin wrote into the space. */
344
+ list(space: string): Promise<PluginSpaceItem[]>;
345
+ /** Takes out one key, or every item of the plugin in the space; what a person changed stays. */
346
+ remove(space: string, key?: string): Promise<number>;
347
+ /**
348
+ * Writes the Übersicht of values: of one, or of every value of a choice (or of every choice)
349
+ * that has three items or more. Returns how many were written. Costs a model call each.
350
+ */
351
+ summarize(space: string, category?: string, value?: string): Promise<number>;
352
+ }
191
353
  /** What a tool of the space assistant knows about the turn that calls it. */
192
354
  export interface PluginAssistantContext {
193
355
  tenantId: string;
@@ -310,6 +472,8 @@ export interface PluginServerApi {
310
472
  every(name: string, everyMs: number, handler: (tick: PluginTick) => void | Promise<void>): void;
311
473
  /** The space's search index, which `project_search` asks. */
312
474
  index: PluginIndex;
475
+ /** Wissen of a space: pages, tables and files the plugin writes, with their Kategorien. */
476
+ spaceData: PluginSpaceData;
313
477
  web: PluginWeb;
314
478
  documents: PluginDocuments;
315
479
  /**
@@ -0,0 +1,199 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Wissen: what a space holds for its wizards' steps to look up. Pages (Markdown, with sub-pages),
4
+ * tables (typed columns; FAQ is a format of one) and files that could not be read into either.
5
+ * Kategorien run across all of them: typed properties a step filters on before it searches.
6
+ */
7
+ export declare const CATEGORY_TYPES: readonly ["choice", "date", "number", "text", "boolean"];
8
+ export type CategoryType = (typeof CATEGORY_TYPES)[number];
9
+ /** A value of a choice, how many items have it, and whether it has an Übersicht. */
10
+ export interface CategoryValueInfo {
11
+ id: string;
12
+ value: string;
13
+ count: number;
14
+ summary: boolean;
15
+ }
16
+ export interface SpaceCategory {
17
+ id: string;
18
+ name: string;
19
+ type: CategoryType;
20
+ /** A number's unit: `h`, `€`, `kg`. */
21
+ unit: string | null;
22
+ /** A choice: several values per item. */
23
+ multiple: boolean;
24
+ /** A choice whose values are ranked: their order is the rank. */
25
+ ordered: boolean;
26
+ /** Where its values come from, in a few words. */
27
+ hint: string | null;
28
+ /** The plugin that brought it. */
29
+ origin: string | null;
30
+ /** Proposed, not taken yet: never filled until a person takes it. */
31
+ proposed: boolean;
32
+ /** A column of a table whose cells are its values. */
33
+ tableId: string | null;
34
+ columnId: string | null;
35
+ /** How many items have it. */
36
+ count: number;
37
+ /** A choice's values, in their order. */
38
+ values: CategoryValueInfo[];
39
+ /** A date's or a number's lowest and highest value (a date as `YYYY-MM-DD`). */
40
+ range: {
41
+ min: string | number;
42
+ max: string | number;
43
+ } | null;
44
+ /** Pairs of values that look like one written two ways ("Graz-Süd", "Graz Süd"), by id. */
45
+ alike: [string, string][];
46
+ }
47
+ /** A value as an item has it: a choice's value, a date as `YYYY-MM-DD`, a number, a text, yes/no. */
48
+ export type CategoryCell = string | number | boolean;
49
+ /** What an item has of one Kategorie. */
50
+ export interface ItemCategory {
51
+ categoryId: string;
52
+ /** A choice with several values has several. */
53
+ values: CategoryCell[];
54
+ by: "person" | "origin" | "model";
55
+ }
56
+ /** What a writer sets: by the Kategorie's name; a list for a choice with several values. */
57
+ export type CategoryInput = Record<string, CategoryCell | CategoryCell[] | null>;
58
+ export type KnowledgeKind = "page" | "table" | "file";
59
+ /** An item of Wissen, as lists show it. Sub-pages stand under their page, not in the list. */
60
+ export interface KnowledgeItem {
61
+ /** `p:<id>`, `t:<id>`, `f:<id>`. */
62
+ key: string;
63
+ kind: KnowledgeKind;
64
+ id: string;
65
+ title: string;
66
+ /** How a step names it: `pages/handbuch/montage`, `tables/kettenprogramm`, `files/plan.dwg`. */
67
+ path: string;
68
+ /** A page's sub-pages. */
69
+ children: number;
70
+ /** A table's rows. */
71
+ rows: number | null;
72
+ format: "faq" | null;
73
+ /** What wrote it: a plugin's id and what it calls the source; null when a person did. */
74
+ origin: string | null;
75
+ originLabel: string | null;
76
+ /** A person changed what its origin wrote: the origin no longer writes it. */
77
+ kept: boolean;
78
+ /** Why a person should look at it; null when nothing is in doubt. */
79
+ review: string | null;
80
+ /** The file it was read from. */
81
+ fileId: string | null;
82
+ /** A file still being read, or that could not be read. */
83
+ status: "pending" | "ready" | "failed";
84
+ categories: ItemCategory[];
85
+ updatedAt: string;
86
+ }
87
+ export interface Knowledge {
88
+ items: KnowledgeItem[];
89
+ categories: SpaceCategory[];
90
+ /** The index finds by meaning too, not by keywords alone. */
91
+ embeddings: boolean;
92
+ /** A classifier checks what a search found. */
93
+ checked: boolean;
94
+ }
95
+ /** A value of a choice, opened: its Übersicht and its Inhaltsverzeichnis, the items that have it. */
96
+ export interface KnowledgeValue {
97
+ id: string;
98
+ value: string;
99
+ category: {
100
+ id: string;
101
+ name: string;
102
+ };
103
+ summary: string | null;
104
+ summaryAt: string | null;
105
+ /** Pages (sub-pages too), tables and files that have the value. */
106
+ items: KnowledgeItem[];
107
+ /** Rows of tables that have it, by table. */
108
+ rows: {
109
+ tableId: string;
110
+ title: string;
111
+ count: number;
112
+ }[];
113
+ }
114
+ /** A page with what Wissen knows about it, as the studio opens it. */
115
+ export interface KnowledgePage {
116
+ id: string;
117
+ path: string;
118
+ /** Its ancestors, outermost first. */
119
+ parents: {
120
+ id: string;
121
+ title: string;
122
+ }[];
123
+ children: {
124
+ id: string;
125
+ title: string;
126
+ path: string;
127
+ }[];
128
+ categories: ItemCategory[];
129
+ origin: string | null;
130
+ originLabel: string | null;
131
+ kept: boolean;
132
+ review: string | null;
133
+ file: {
134
+ id: string;
135
+ name: string;
136
+ mime: string;
137
+ } | null;
138
+ }
139
+ /**
140
+ * A filter on one Kategorie: a value (a choice is it, a text equals it, yes/no), one of several,
141
+ * or a range — numbers by `gte`/`lte`, dates by `after`/`before` (`YYYY-MM-DD`, both ends
142
+ * included), a text by `contains`, an ordered choice by `atLeast`.
143
+ */
144
+ export declare const whereValueSchema: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>, z.ZodObject<{
145
+ gte: z.ZodOptional<z.ZodNumber>;
146
+ lte: z.ZodOptional<z.ZodNumber>;
147
+ after: z.ZodOptional<z.ZodString>;
148
+ before: z.ZodOptional<z.ZodString>;
149
+ contains: z.ZodOptional<z.ZodString>;
150
+ atLeast: z.ZodOptional<z.ZodString>;
151
+ }, z.core.$strict>]>;
152
+ export type WhereValue = z.infer<typeof whereValueSchema>;
153
+ /** Filters by the Kategorie's name; an item matches all of them. */
154
+ export declare const whereSchema: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>, z.ZodObject<{
155
+ gte: z.ZodOptional<z.ZodNumber>;
156
+ lte: z.ZodOptional<z.ZodNumber>;
157
+ after: z.ZodOptional<z.ZodString>;
158
+ before: z.ZodOptional<z.ZodString>;
159
+ contains: z.ZodOptional<z.ZodString>;
160
+ atLeast: z.ZodOptional<z.ZodString>;
161
+ }, z.core.$strict>]>>;
162
+ export type Where = z.infer<typeof whereSchema>;
163
+ export interface KnowledgeHit {
164
+ /** `p:<id>`, `t:<id>` (a row of it), `f:<id>`, `v:<id>` (a value's Übersicht), `d:<id>` (a
165
+ * document read before Wissen had pages), `x:<plugin>:<key>` (a plugin's text). */
166
+ key: string;
167
+ kind: KnowledgeKind | "summary" | "document" | "plugin";
168
+ /** What the studio opens for it: `p:`, `t:` (a row's table), `f:`, `v:`; null for a plugin's text. */
169
+ open: string | null;
170
+ path: string;
171
+ title: string;
172
+ /** The Kategorien as `name: value`. */
173
+ categories: string[];
174
+ /** The best section's headings, outermost first, joined by " › ". */
175
+ heading: string | null;
176
+ /** The page of the original it stands on. */
177
+ sheet: number | null;
178
+ /** A row of a table: its id. */
179
+ rowId: string | null;
180
+ text: string;
181
+ /** P(relevant) the classifier gave; null when nothing checked it. */
182
+ relevance: number | null;
183
+ /** Where the studio shows a plugin's text. */
184
+ link: string | null;
185
+ plugin: string | null;
186
+ }
187
+ export interface KnowledgeSearch {
188
+ hits: KnowledgeHit[];
189
+ /** How many units the keywords and the vectors found together, before the classifier. */
190
+ candidates: number;
191
+ /** A classifier judged the candidates; false: they keep the order the index gave them. */
192
+ checked: boolean;
193
+ /** Items the filter left, when there was one. */
194
+ filtered: number | null;
195
+ }
196
+ /** A day as `YYYY-MM-DD` from what a person or a source wrote: ISO, `14.03.2026`, a timestamp. */
197
+ export declare function dayOf(value: unknown): string | null;
198
+ /** A slug for a path: lower case, `-` between words, umlauts spelled out. */
199
+ export declare function slugOf(title: string): string;
@@ -0,0 +1,68 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Wissen: what a space holds for its wizards' steps to look up. Pages (Markdown, with sub-pages),
4
+ * tables (typed columns; FAQ is a format of one) and files that could not be read into either.
5
+ * Kategorien run across all of them: typed properties a step filters on before it searches.
6
+ */
7
+ export const CATEGORY_TYPES = ["choice", "date", "number", "text", "boolean"];
8
+ // --- filters --------------------------------------------------------------------
9
+ /**
10
+ * A filter on one Kategorie: a value (a choice is it, a text equals it, yes/no), one of several,
11
+ * or a range — numbers by `gte`/`lte`, dates by `after`/`before` (`YYYY-MM-DD`, both ends
12
+ * included), a text by `contains`, an ordered choice by `atLeast`.
13
+ */
14
+ export const whereValueSchema = z.union([
15
+ z.string().max(200),
16
+ z.number(),
17
+ z.boolean(),
18
+ z.array(z.union([z.string().max(200), z.number()])).max(50),
19
+ z
20
+ .object({
21
+ gte: z.number().optional(),
22
+ lte: z.number().optional(),
23
+ after: z.string().max(40).optional(),
24
+ before: z.string().max(40).optional(),
25
+ contains: z.string().max(200).optional(),
26
+ atLeast: z.string().max(200).optional(),
27
+ })
28
+ .strict(),
29
+ ]);
30
+ /** Filters by the Kategorie's name; an item matches all of them. */
31
+ export const whereSchema = z.record(z.string().max(120), whereValueSchema);
32
+ /** A day as `YYYY-MM-DD` from what a person or a source wrote: ISO, `14.03.2026`, a timestamp. */
33
+ export function dayOf(value) {
34
+ if (typeof value === "number" && Number.isFinite(value)) {
35
+ const d = new Date(value);
36
+ return Number.isNaN(d.getTime()) ? null : d.toISOString().slice(0, 10);
37
+ }
38
+ if (typeof value !== "string") {
39
+ return null;
40
+ }
41
+ const s = value.trim();
42
+ const iso = /^(\d{4})-(\d{2})-(\d{2})/.exec(s);
43
+ if (iso) {
44
+ return `${iso[1]}-${iso[2]}-${iso[3]}`;
45
+ }
46
+ const de = /^(\d{1,2})\.(\d{1,2})\.(\d{2,4})/.exec(s);
47
+ if (de) {
48
+ const year = de[3].length === 2 ? `20${de[3]}` : de[3];
49
+ return `${year}-${de[2].padStart(2, "0")}-${de[1].padStart(2, "0")}`;
50
+ }
51
+ return null;
52
+ }
53
+ /** A slug for a path: lower case, `-` between words, umlauts spelled out. */
54
+ export function slugOf(title) {
55
+ return (title
56
+ .toLowerCase()
57
+ .replace(/ä/g, "ae")
58
+ .replace(/ö/g, "oe")
59
+ .replace(/ü/g, "ue")
60
+ .replace(/ß/g, "ss")
61
+ .replace(/§/g, "p")
62
+ .normalize("NFKD")
63
+ .replace(/[̀-ͯ]/g, "")
64
+ .replace(/[^a-z0-9]+/g, "-")
65
+ .replace(/^-+|-+$/g, "")
66
+ .slice(0, 60)
67
+ .replace(/-+$/, "") || "seite");
68
+ }
@@ -52,7 +52,7 @@ export declare const PROJECT_LIMITS: {
52
52
  readonly files: {
53
53
  readonly logo: 8;
54
54
  readonly asset: 100;
55
- readonly document: 50;
55
+ readonly document: 2000;
56
56
  };
57
57
  readonly bytes: {
58
58
  readonly logo: 5000000;
@@ -63,18 +63,3 @@ export declare const PROJECT_LIMITS: {
63
63
  };
64
64
  /** The largest file of a kind and type a project takes. */
65
65
  export declare function projectFileLimit(kind: ProjectFileKind, mime: string): number;
66
- /** One passage the document index found for a question. */
67
- export interface ProjectSearchHit {
68
- /** The document the passage is of; null for a plugin's text. */
69
- fileId: string | null;
70
- /** The plugin whose text it is; null for a document. */
71
- plugin: string | null;
72
- /** The document's name, or the title of the plugin's text. */
73
- name: string;
74
- /** Where the studio shows a plugin's text. */
75
- link: string | null;
76
- /** The passage's place in its document, from 0. */
77
- index: number;
78
- text: string;
79
- score: number;
80
- }
@@ -84,7 +84,7 @@ export const PROJECT_FILE_KINDS = ["logo", "asset", "document"];
84
84
  export const PROJECT_LIMITS = {
85
85
  facts: 100,
86
86
  colors: 12,
87
- files: { logo: 8, asset: 100, document: 50 },
87
+ files: { logo: 8, asset: 100, document: 2000 },
88
88
  bytes: { logo: 5_000_000, asset: 15_000_000, video: 80_000_000, document: 30_000_000 },
89
89
  };
90
90
  /** The largest file of a kind and type a project takes. */
@@ -225,6 +225,14 @@ export declare const RUN_NOTES: {
225
225
  readonly de: "Sucht in den Dokumenten: {query}";
226
226
  readonly en: "Searching the documents: {query}";
227
227
  };
228
+ readonly searchesKnowledge: {
229
+ readonly de: "Sucht in Wissen: {query} – {relevant} von {candidates} passen";
230
+ readonly en: "Searching the knowledge: {query} – {relevant} of {candidates} fit";
231
+ };
232
+ readonly listsKnowledge: {
233
+ readonly de: "Listet Wissen";
234
+ readonly en: "Listing the knowledge";
235
+ };
228
236
  readonly searchesMail: {
229
237
  readonly de: "Durchsucht {label}";
230
238
  readonly en: "Searching {label}";
@@ -54,6 +54,11 @@ export const RUN_NOTES = {
54
54
  de: "Sucht in den Dokumenten: {query}",
55
55
  en: "Searching the documents: {query}",
56
56
  },
57
+ searchesKnowledge: {
58
+ de: "Sucht in Wissen: {query} – {relevant} von {candidates} passen",
59
+ en: "Searching the knowledge: {query} – {relevant} of {candidates} fit",
60
+ },
61
+ listsKnowledge: { de: "Listet Wissen", en: "Listing the knowledge" },
57
62
  searchesMail: { de: "Durchsucht {label}", en: "Searching {label}" },
58
63
  savesReceipts: {
59
64
  de: "Speichert {count} Belege aus {label}",
@@ -1,4 +1,5 @@
1
1
  import { type TableColumn } from "./engenty/data-tables/index.js";
2
+ import type { ItemCategory, KnowledgePage } from "./knowledge.js";
2
3
  /**
3
4
  * What a space keeps as data: tables (typed columns, rows of cells, as engenty's data tables)
4
5
  * and pages (a title and markdown). Each belongs to the space, or to one of its wizards.
@@ -39,10 +40,26 @@ export interface SpaceTable {
39
40
  wizardId: string | null;
40
41
  list: string | null;
41
42
  title: string;
43
+ /** `faq`: a question and its answer per row. */
44
+ format: "faq" | null;
42
45
  columns: TableColumn[];
43
46
  rows: SpaceTableRow[];
44
47
  /** The space is not changed here: a local install's, or nothing is built on this runtime. */
45
48
  readOnly: boolean;
49
+ /** A table of Wissen (the space's own): where it came from and its Kategorien. */
50
+ knowledge: {
51
+ path: string;
52
+ categories: ItemCategory[];
53
+ origin: string | null;
54
+ originLabel: string | null;
55
+ kept: boolean;
56
+ review: string | null;
57
+ file: {
58
+ id: string;
59
+ name: string;
60
+ mime: string;
61
+ } | null;
62
+ } | null;
46
63
  }
47
64
  export interface SpacePage {
48
65
  id: string;
@@ -52,9 +69,12 @@ export interface SpacePage {
52
69
  markdown: string;
53
70
  updatedAt: string;
54
71
  readOnly: boolean;
72
+ /** A page of Wissen (the space's own): where it stands, its sub-pages, its Kategorien. */
73
+ knowledge: KnowledgePage | null;
55
74
  }
56
75
  export declare const SPACE_DATA_LIMITS: {
57
76
  tables: number;
77
+ /** Sub-pages count: a law split along its paragraphs is a few thousand. */
58
78
  pages: number;
59
79
  rowsPerTable: number;
60
80
  pageChars: number;
@@ -1,7 +1,8 @@
1
1
  import { coerceColumnValue, TableColumnValueError, } from "./engenty/data-tables/index.js";
2
2
  export const SPACE_DATA_LIMITS = {
3
3
  tables: 200,
4
- pages: 500,
4
+ /** Sub-pages count: a law split along its paragraphs is a few thousand. */
5
+ pages: 5000,
5
6
  rowsPerTable: 5000,
6
7
  pageChars: 200_000,
7
8
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wizards",
3
- "version": "0.2.17",
3
+ "version": "0.2.18",
4
4
  "description": "Make a wish. Get your wizard. It guides you step by step. Let AI do the work. Runs on your machine, on the AI subscription you already have.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "homepage": "https://engenty.ai",