wizards 0.2.17 → 0.2.19
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/apps/runtime/dist/agents/project-assistant.js +20 -2
- package/apps/runtime/dist/authoring/guide.js +6 -3
- package/apps/runtime/dist/db/migrations/tenant/0009_knowledge.sql +104 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0009_snapshot.json +2837 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +7 -0
- package/apps/runtime/dist/db/schema.js +160 -8
- package/apps/runtime/dist/engine/steps.js +3 -11
- package/apps/runtime/dist/env.js +2 -0
- package/apps/runtime/dist/files/storage.js +15 -0
- package/apps/runtime/dist/models.js +38 -0
- package/apps/runtime/dist/plugins/loader.js +2 -0
- package/apps/runtime/dist/routes/studio.js +147 -6
- package/apps/runtime/dist/services/cloud.js +59 -1
- package/apps/runtime/dist/services/knowledge-model.js +558 -0
- package/apps/runtime/dist/services/knowledge.js +721 -0
- package/apps/runtime/dist/services/project-files.js +114 -6
- package/apps/runtime/dist/services/project-index.js +632 -94
- package/apps/runtime/dist/services/relevance.js +90 -0
- package/apps/runtime/dist/services/space-categories.js +882 -0
- package/apps/runtime/dist/services/space-data.js +466 -38
- package/apps/runtime/dist/services/space-origin.js +164 -0
- package/apps/runtime/dist/services/spaces.js +155 -1
- package/apps/runtime/dist/tools/pages.js +27 -22
- package/apps/runtime/dist/tools/project.js +130 -40
- package/apps/web/dist/assets/{Account-LVXhagaS.js → Account-O6OTAY88.js} +1 -1
- package/apps/web/dist/assets/{ChangelogDialog-DM0KAoy6.js → ChangelogDialog-DLrXuyLL.js} +1 -1
- package/apps/web/dist/assets/{ChatPanel-CxOQmtv-.js → ChatPanel-BPD-iQK0.js} +1 -1
- package/apps/web/dist/assets/{CreditsDialog-DkW3YT44.js → CreditsDialog-Ckema8Ss.js} +1 -1
- package/apps/web/dist/assets/{EditorPage-hbisE1Wf.js → EditorPage-Dtu9j41h.js} +1 -1
- package/apps/web/dist/assets/{FlowPreview-v7or29ku.js → FlowPreview-DB-86F8z.js} +1 -1
- package/apps/web/dist/assets/{Harness-RZF-bBmb.js → Harness-D-DSa4ip.js} +1 -1
- package/apps/web/dist/assets/{HomePage-mxG-a_KU.js → HomePage-ZrCxIiJT.js} +1 -1
- package/apps/web/dist/assets/{ImportWizard-CiPyUt3E.js → ImportWizard-DtZH-PgS.js} +1 -1
- package/apps/web/dist/assets/{LocalRuntime-Dqp9wkN3.js → LocalRuntime-o1OAvp7c.js} +1 -1
- package/apps/web/dist/assets/NewWizardPage-Bbt-w8tE.js +2 -0
- package/apps/web/dist/assets/ProjectPage-MHxFqA98.js +5 -0
- package/apps/web/dist/assets/{PublicRunner-TGkOwbAP.js → PublicRunner-CYo2F8H4.js} +1 -1
- package/apps/web/dist/assets/{RunnerView-DzHGshZj.js → RunnerView-Bqp0paLi.js} +1 -1
- package/apps/web/dist/assets/{SettingsPage-CG3Xtfej.js → SettingsPage-XyRrZeMr.js} +1 -1
- package/apps/web/dist/assets/{SetupPage-O1GfPlqX.js → SetupPage-CdBYFmh1.js} +1 -1
- package/apps/web/dist/assets/{SharePage-CCNUhtry.js → SharePage-B7ON0pe1.js} +1 -1
- package/apps/web/dist/assets/{api-DB22_B7G.js → api-CVG-xDEn.js} +1 -1
- package/apps/web/dist/assets/changelog-B2U80Wtl.js +1 -0
- package/apps/web/dist/assets/{esm-3TT9i_rv.js → esm-DutMJAod.js} +1 -1
- package/apps/web/dist/assets/index-Bwyehu0k.css +1 -0
- package/apps/web/dist/assets/{index-CLe9Wrpu.js → index-hpNaR92X.js} +3 -3
- package/apps/web/dist/assets/{meta-CEsBCPfA.js → meta--kpiTOS-.js} +1 -1
- package/apps/web/dist/assets/{settings-menu-pOcjznGJ.js → settings-menu-BXpT5jOB.js} +1 -1
- package/apps/web/dist/assets/ui-BPY2pIh0.js +1 -0
- package/apps/web/dist/index.html +4 -4
- package/apps/web/dist/mcp/flow.html +2 -2
- package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +164 -0
- package/node_modules/@engenty-wizards/shared/dist/knowledge.d.ts +199 -0
- package/node_modules/@engenty-wizards/shared/dist/knowledge.js +68 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +13 -1
- package/node_modules/@engenty-wizards/shared/dist/marketplace.js +3 -0
- package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +1 -16
- package/node_modules/@engenty-wizards/shared/dist/projects.js +1 -1
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +8 -0
- package/node_modules/@engenty-wizards/shared/dist/run.js +5 -0
- package/node_modules/@engenty-wizards/shared/dist/space-data.d.ts +20 -0
- package/node_modules/@engenty-wizards/shared/dist/space-data.js +2 -1
- package/package.json +1 -1
- package/apps/web/dist/assets/NewWizardPage-DHwRsi0_.js +0 -2
- package/apps/web/dist/assets/ProjectPage-Bb7jN_6n.js +0 -4
- package/apps/web/dist/assets/changelog-BkIJdZ7M.js +0 -1
- package/apps/web/dist/assets/index-BPpKz65x.css +0 -1
- package/apps/web/dist/assets/ui-I7QYMX0F.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sql } from "drizzle-orm";
|
|
2
|
-
import { blob, index, integer, primaryKey, sqliteTable, text, uniqueIndex, } from "drizzle-orm/sqlite-core";
|
|
2
|
+
import { blob, index, integer, primaryKey, real, sqliteTable, text, uniqueIndex, } from "drizzle-orm/sqlite-core";
|
|
3
3
|
const now = sql `(unixepoch() * 1000)`;
|
|
4
4
|
const createdAt = () => integer("created_at", { mode: "timestamp_ms" }).notNull().default(now);
|
|
5
5
|
const updatedAt = () => integer("updated_at", { mode: "timestamp_ms" }).notNull().default(now);
|
|
@@ -196,14 +196,23 @@ export const projectFile = sqliteTable("project_file", {
|
|
|
196
196
|
chars: integer("chars"),
|
|
197
197
|
/** What the index holds of a document; `embeddings` includes the keywords. */
|
|
198
198
|
indexed: text("indexed", { enum: ["embeddings", "keywords"] }),
|
|
199
|
+
/** A plugin's file (`server.spaceData.putFile`): the plugin, its key and what it calls it. */
|
|
200
|
+
origin: text("origin"),
|
|
201
|
+
originKey: text("origin_key"),
|
|
202
|
+
originLabel: text("origin_label"),
|
|
199
203
|
createdAt: createdAt(),
|
|
200
204
|
updatedAt: updatedAt(),
|
|
201
|
-
}, (t) => [
|
|
205
|
+
}, (t) => [
|
|
206
|
+
index("project_file_project").on(t.projectId, t.kind, t.position),
|
|
207
|
+
uniqueIndex("project_file_origin").on(t.projectId, t.origin, t.originKey),
|
|
208
|
+
]);
|
|
202
209
|
/**
|
|
203
|
-
* The
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
210
|
+
* The index of Wissen: one unit per section of a page, row of a table, file, summary of a
|
|
211
|
+
* Kategorie's value, passage of a document read before pages, and passage of a plugin's text
|
|
212
|
+
* (`server.index`). Each unit's text starts with where it stands and its Kategorien. Keywords
|
|
213
|
+
* live in two FTS5 tables, words (`project_chunk_fts`) and trigrams (`project_chunk_tri`), which
|
|
214
|
+
* triggers keep in step (see the migrations); `embedding` is the unit's vector as float32
|
|
215
|
+
* bytes, compared with libSQL's `vector_distance_cos`.
|
|
207
216
|
*/
|
|
208
217
|
export const projectChunk = sqliteTable("project_chunk", {
|
|
209
218
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
@@ -212,6 +221,23 @@ export const projectChunk = sqliteTable("project_chunk", {
|
|
|
212
221
|
.references(() => project.id, { onDelete: "cascade" }),
|
|
213
222
|
/** The document the passage is of; null for a plugin's text. */
|
|
214
223
|
fileId: text("file_id").references(() => projectFile.id, { onDelete: "cascade" }),
|
|
224
|
+
/** What the unit is of: a page, a row (with its table), a table (FAQ format: a row), a value. */
|
|
225
|
+
pageId: text("page_id").references(() => spacePage.id, {
|
|
226
|
+
onDelete: "cascade",
|
|
227
|
+
}),
|
|
228
|
+
rowId: text("row_id").references(() => spaceTableRow.id, {
|
|
229
|
+
onDelete: "cascade",
|
|
230
|
+
}),
|
|
231
|
+
tableId: text("table_id").references(() => spaceTable.id, {
|
|
232
|
+
onDelete: "cascade",
|
|
233
|
+
}),
|
|
234
|
+
valueId: text("value_id").references(() => spaceCategoryValue.id, {
|
|
235
|
+
onDelete: "cascade",
|
|
236
|
+
}),
|
|
237
|
+
/** A section's headings, outermost first, joined by " › ". */
|
|
238
|
+
heading: text("heading"),
|
|
239
|
+
/** The page of the original a section starts on (`<!-- S. 3 -->`). */
|
|
240
|
+
sheet: integer("sheet"),
|
|
215
241
|
/** A plugin's text: the plugin, its key for the text, its title and where the studio shows it. */
|
|
216
242
|
plugin: text("plugin"),
|
|
217
243
|
ref: text("ref"),
|
|
@@ -226,6 +252,10 @@ export const projectChunk = sqliteTable("project_chunk", {
|
|
|
226
252
|
index("project_chunk_project").on(t.projectId, t.model),
|
|
227
253
|
index("project_chunk_file").on(t.fileId),
|
|
228
254
|
index("project_chunk_ref").on(t.projectId, t.plugin, t.ref),
|
|
255
|
+
index("project_chunk_page").on(t.pageId),
|
|
256
|
+
index("project_chunk_row").on(t.rowId),
|
|
257
|
+
index("project_chunk_table").on(t.tableId),
|
|
258
|
+
index("project_chunk_value").on(t.valueId),
|
|
229
259
|
]);
|
|
230
260
|
// --- The space's data -------------------------------------------------------
|
|
231
261
|
// Tables and pages a space keeps, its own or one of its wizards'. Shared by everyone who works
|
|
@@ -247,12 +277,30 @@ export const spaceTable = sqliteTable("space_table", {
|
|
|
247
277
|
/** The column a row is matched on, as the list says. */
|
|
248
278
|
keyColumn: text("key_column"),
|
|
249
279
|
title: text("title").notNull(),
|
|
280
|
+
/** Its name in a path (`tables/<slug>`), unique among the space's tables. */
|
|
281
|
+
slug: text("slug"),
|
|
282
|
+
/** `faq`: a question and its answer per row. */
|
|
283
|
+
format: text("format", { enum: ["faq"] }),
|
|
250
284
|
columns: text("columns", { mode: "json" }).$type().notNull(),
|
|
285
|
+
/**
|
|
286
|
+
* Written by a plugin (`server.spaceData`): the plugin, its key for the item and what it
|
|
287
|
+
* calls where it came from ("Tagesberichte"). Null: made in the studio or uploaded.
|
|
288
|
+
*/
|
|
289
|
+
origin: text("origin"),
|
|
290
|
+
originKey: text("origin_key"),
|
|
291
|
+
originLabel: text("origin_label"),
|
|
292
|
+
/** A person changed what its origin wrote: the origin no longer writes it. */
|
|
293
|
+
kept: integer("kept", { mode: "boolean" }).notNull().default(false),
|
|
294
|
+
/** Why a person should look at it ("hardly readable"); null when nothing is in doubt. */
|
|
295
|
+
review: text("review"),
|
|
296
|
+
/** The file it was read from, kept as the evidence. */
|
|
297
|
+
fileId: text("file_id").references(() => projectFile.id, { onDelete: "set null" }),
|
|
251
298
|
createdAt: createdAt(),
|
|
252
299
|
updatedAt: updatedAt(),
|
|
253
300
|
}, (t) => [
|
|
254
301
|
index("space_table_project").on(t.projectId, t.wizardId),
|
|
255
302
|
uniqueIndex("space_table_list").on(t.wizardId, t.list),
|
|
303
|
+
uniqueIndex("space_table_origin").on(t.projectId, t.origin, t.originKey),
|
|
256
304
|
]);
|
|
257
305
|
/** A row of a table: cells by column id. Rows stand in the order they were added. */
|
|
258
306
|
export const spaceTableRow = sqliteTable("space_table_row", {
|
|
@@ -269,7 +317,10 @@ export const spaceTableRow = sqliteTable("space_table_row", {
|
|
|
269
317
|
index("space_table_row_table").on(t.tableId, t.createdAt),
|
|
270
318
|
index("space_table_row_key").on(t.tableId, t.key),
|
|
271
319
|
]);
|
|
272
|
-
/**
|
|
320
|
+
/**
|
|
321
|
+
* A page: a title and markdown, what the editor shows and an agent reads and writes. A page of
|
|
322
|
+
* the space's own may have sub-pages: a long document split along its own headings.
|
|
323
|
+
*/
|
|
273
324
|
export const spacePage = sqliteTable("space_page", {
|
|
274
325
|
id: text("id").primaryKey(),
|
|
275
326
|
tenantId: text("tenant_id").notNull(),
|
|
@@ -277,11 +328,112 @@ export const spacePage = sqliteTable("space_page", {
|
|
|
277
328
|
.notNull()
|
|
278
329
|
.references(() => project.id, { onDelete: "cascade" }),
|
|
279
330
|
wizardId: text("wizard_id").references(() => wizard.id, { onDelete: "cascade" }),
|
|
331
|
+
parentId: text("parent_id").references(() => spacePage.id, {
|
|
332
|
+
onDelete: "cascade",
|
|
333
|
+
}),
|
|
334
|
+
/** Its place among its parent's sub-pages. */
|
|
335
|
+
position: integer("position").notNull().default(0),
|
|
280
336
|
title: text("title").notNull(),
|
|
337
|
+
/** Its name in a path (`pages/<parent>/<slug>`), unique among its siblings. */
|
|
338
|
+
slug: text("slug"),
|
|
281
339
|
markdown: text("markdown").notNull().default(""),
|
|
340
|
+
/**
|
|
341
|
+
* Written by a plugin (`server.spaceData`): the plugin, its key for the item and what it
|
|
342
|
+
* calls where it came from ("Tagesberichte"). Null: made in the studio or uploaded.
|
|
343
|
+
*/
|
|
344
|
+
origin: text("origin"),
|
|
345
|
+
originKey: text("origin_key"),
|
|
346
|
+
originLabel: text("origin_label"),
|
|
347
|
+
/** A person changed what its origin wrote: the origin no longer writes it. */
|
|
348
|
+
kept: integer("kept", { mode: "boolean" }).notNull().default(false),
|
|
349
|
+
/** Why a person should look at it ("hardly readable"); null when nothing is in doubt. */
|
|
350
|
+
review: text("review"),
|
|
351
|
+
/** The file it was read from, kept as the evidence. */
|
|
352
|
+
fileId: text("file_id").references(() => projectFile.id, { onDelete: "set null" }),
|
|
282
353
|
createdAt: createdAt(),
|
|
283
354
|
updatedAt: updatedAt(),
|
|
284
|
-
}, (t) => [
|
|
355
|
+
}, (t) => [
|
|
356
|
+
index("space_page_project").on(t.projectId, t.wizardId),
|
|
357
|
+
index("space_page_parent").on(t.parentId, t.position),
|
|
358
|
+
uniqueIndex("space_page_origin").on(t.projectId, t.origin, t.originKey),
|
|
359
|
+
]);
|
|
360
|
+
// --- Kategorien -----------------------------------------------------------------
|
|
361
|
+
// Typed properties that run across Wissen: set on pages, rows, tables and files, any number of
|
|
362
|
+
// each, so a step filters before it searches.
|
|
363
|
+
/** A Kategorie of the space: a name, a type and, for a choice, the values it has met. */
|
|
364
|
+
export const spaceCategory = sqliteTable("space_category", {
|
|
365
|
+
id: text("id").primaryKey(),
|
|
366
|
+
tenantId: text("tenant_id").notNull(),
|
|
367
|
+
projectId: text("project_id")
|
|
368
|
+
.notNull()
|
|
369
|
+
.references(() => project.id, { onDelete: "cascade" }),
|
|
370
|
+
name: text("name").notNull(),
|
|
371
|
+
type: text("type", { enum: ["choice", "date", "number", "text", "boolean"] }).notNull(),
|
|
372
|
+
/** A number's unit: `h`, `€`, `kg`. */
|
|
373
|
+
unit: text("unit"),
|
|
374
|
+
/** A choice: several values per item. */
|
|
375
|
+
multiple: integer("multiple", { mode: "boolean" }).notNull().default(false),
|
|
376
|
+
/** A choice whose values are ranked (niedrig, mittel, hoch): their position is the rank. */
|
|
377
|
+
ordered: integer("ordered", { mode: "boolean" }).notNull().default(false),
|
|
378
|
+
/** Where its values come from, said in a few words ("Kopfzeile", "Spalte WLL"). */
|
|
379
|
+
hint: text("hint"),
|
|
380
|
+
/** The plugin that brought it; null: made in the studio. */
|
|
381
|
+
origin: text("origin"),
|
|
382
|
+
/** Proposed, not yet taken: shown to be confirmed, never filled. */
|
|
383
|
+
proposed: integer("proposed", { mode: "boolean" }).notNull().default(false),
|
|
384
|
+
/** A column of a table whose cells are its values for the table's rows. */
|
|
385
|
+
tableId: text("table_id").references(() => spaceTable.id, { onDelete: "set null" }),
|
|
386
|
+
columnId: text("column_id"),
|
|
387
|
+
position: integer("position").notNull().default(0),
|
|
388
|
+
createdAt: createdAt(),
|
|
389
|
+
updatedAt: updatedAt(),
|
|
390
|
+
}, (t) => [uniqueIndex("space_category_name").on(t.projectId, t.name)]);
|
|
391
|
+
/** A value of a choice: grows as items arrive; its summary is the Übersicht of its items. */
|
|
392
|
+
export const spaceCategoryValue = sqliteTable("space_category_value", {
|
|
393
|
+
id: text("id").primaryKey(),
|
|
394
|
+
categoryId: text("category_id")
|
|
395
|
+
.notNull()
|
|
396
|
+
.references(() => spaceCategory.id, { onDelete: "cascade" }),
|
|
397
|
+
value: text("value").notNull(),
|
|
398
|
+
position: integer("position").notNull().default(0),
|
|
399
|
+
/** The Übersicht: Markdown a model wrote of the value's items, when it last did. */
|
|
400
|
+
summary: text("summary"),
|
|
401
|
+
summaryAt: integer("summary_at", { mode: "timestamp_ms" }),
|
|
402
|
+
createdAt: createdAt(),
|
|
403
|
+
}, (t) => [uniqueIndex("space_category_value_value").on(t.categoryId, t.value)]);
|
|
404
|
+
/**
|
|
405
|
+
* A Kategorie set on an item: exactly one of page, row, table and file. A choice points to its
|
|
406
|
+
* value; the other types keep theirs typed: a date as milliseconds, a number, a short text, yes
|
|
407
|
+
* or no. A choice with several values has a row per value.
|
|
408
|
+
*/
|
|
409
|
+
export const spaceItemCategory = sqliteTable("space_item_category", {
|
|
410
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
411
|
+
projectId: text("project_id")
|
|
412
|
+
.notNull()
|
|
413
|
+
.references(() => project.id, { onDelete: "cascade" }),
|
|
414
|
+
categoryId: text("category_id")
|
|
415
|
+
.notNull()
|
|
416
|
+
.references(() => spaceCategory.id, { onDelete: "cascade" }),
|
|
417
|
+
pageId: text("page_id").references(() => spacePage.id, { onDelete: "cascade" }),
|
|
418
|
+
rowId: text("row_id").references(() => spaceTableRow.id, { onDelete: "cascade" }),
|
|
419
|
+
tableId: text("table_id").references(() => spaceTable.id, { onDelete: "cascade" }),
|
|
420
|
+
fileId: text("file_id").references(() => projectFile.id, { onDelete: "cascade" }),
|
|
421
|
+
valueId: text("value_id").references(() => spaceCategoryValue.id, { onDelete: "cascade" }),
|
|
422
|
+
text: text("text"),
|
|
423
|
+
num: real("num"),
|
|
424
|
+
at: integer("at"),
|
|
425
|
+
bool: integer("bool", { mode: "boolean" }),
|
|
426
|
+
/** Who set it: a person, the item's origin (a source's structure), or a model reading it. */
|
|
427
|
+
by: text("by", { enum: ["person", "origin", "model"] })
|
|
428
|
+
.notNull()
|
|
429
|
+
.default("person"),
|
|
430
|
+
}, (t) => [
|
|
431
|
+
index("space_item_category_category").on(t.categoryId, t.valueId),
|
|
432
|
+
index("space_item_category_page").on(t.pageId),
|
|
433
|
+
index("space_item_category_row").on(t.rowId),
|
|
434
|
+
index("space_item_category_table").on(t.tableId),
|
|
435
|
+
index("space_item_category_file").on(t.fileId),
|
|
436
|
+
]);
|
|
285
437
|
// --- The wizard's store ------------------------------------------------------
|
|
286
438
|
// What a wizard keeps between runs for one person. `holder` is that person: "u:<userId>" for a
|
|
287
439
|
// signed-in admin, "v:<visitorId>" for an end user on a shared link.
|
|
@@ -6,6 +6,7 @@ import { extFor, loadAsset, loadAssetText } from "../files/storage.js";
|
|
|
6
6
|
import { generateImageMedia, generateSpeechMedia, generateVideoMedia, } from "../media/generate.js";
|
|
7
7
|
import { markMedia } from "../media/marking.js";
|
|
8
8
|
import { attachTools, costOf, isHarnessVendor, textModel } from "../models.js";
|
|
9
|
+
import { knowledgeBlock } from "../services/knowledge.js";
|
|
9
10
|
import { buildStepTools } from "../tools/index.js";
|
|
10
11
|
import { spaceContextOf } from "../tools/plugin.js";
|
|
11
12
|
import { personUploads } from "../tools/store.js";
|
|
@@ -31,16 +32,6 @@ function brandBlock(ctx) {
|
|
|
31
32
|
].filter(Boolean);
|
|
32
33
|
return parts.length ? `# WHO THIS WIZARD BELONGS TO\n${parts.join("\n")}` : "";
|
|
33
34
|
}
|
|
34
|
-
/** The project's documents, for a step that has the tools to search and read them. */
|
|
35
|
-
function documentsBlock(ctx) {
|
|
36
|
-
const docs = ctx.projectFiles.filter((f) => f.kind === "document").slice(0, 40);
|
|
37
|
-
if (!docs.length) {
|
|
38
|
-
return "";
|
|
39
|
-
}
|
|
40
|
-
return `# THE PROJECT'S DOCUMENTS\nKnowledge the wizard's owner gave for all wizards. Find passages with project_search, read a whole document with project_document. Use them when the task touches what they cover.\n${docs
|
|
41
|
-
.map((d) => `- ${d.name}${d.description ? ` — ${d.description}` : ""}`)
|
|
42
|
-
.join("\n")}`;
|
|
43
|
-
}
|
|
44
35
|
/** Previous output of a step, for a revision. */
|
|
45
36
|
async function previousResult(ctx, stepId) {
|
|
46
37
|
const out = ctx.state.outputs[stepId];
|
|
@@ -173,7 +164,8 @@ export async function runAgentStep(step, ctx) {
|
|
|
173
164
|
GROUND_RULES,
|
|
174
165
|
`Today is ${today(ctx)}.`,
|
|
175
166
|
brandBlock(ctx),
|
|
176
|
-
|
|
167
|
+
// Wissen: what the space holds for its wizards, and how to find and read it.
|
|
168
|
+
await knowledgeBlock(ctx.project.id),
|
|
177
169
|
// What the space's plugins hold for its wizards: a wiki, questions and answers.
|
|
178
170
|
...(await spaceContextOf(ctx)).blocks,
|
|
179
171
|
formatHint,
|
package/apps/runtime/dist/env.js
CHANGED
|
@@ -158,6 +158,8 @@ export const env = {
|
|
|
158
158
|
falKey: str("FAL_KEY"),
|
|
159
159
|
elevenlabsKey: str("ELEVENLABS_API_KEY"),
|
|
160
160
|
replicateKey: str("REPLICATE_API_TOKEN"),
|
|
161
|
+
/** TypeSafe's own API, for the classifier that checks what a search found (Jev). */
|
|
162
|
+
typesafeKey: str("TYPESAFE_API_KEY"),
|
|
161
163
|
ollamaUrl: str("OLLAMA_URL", "http://127.0.0.1:11434/v1"),
|
|
162
164
|
/** What each model class runs on when this runtime resolves classes itself (own keys, Ollama). */
|
|
163
165
|
models: {
|
|
@@ -77,6 +77,21 @@ export async function inlineAssetRefs(html) {
|
|
|
77
77
|
}
|
|
78
78
|
return out;
|
|
79
79
|
}
|
|
80
|
+
/** Gives an asset new content under the id it has: what points to it keeps pointing to it. */
|
|
81
|
+
export async function replaceAsset(id, input) {
|
|
82
|
+
const row = await db.query.asset.findFirst({ where: eq(schema.asset.id, id) });
|
|
83
|
+
if (!row) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const bytes = Buffer.from(input.data);
|
|
87
|
+
const file = `${id}-${nanoid(6)}.${extFor(input.mime)}`;
|
|
88
|
+
await objects.put(assetKey(file), bytes);
|
|
89
|
+
await db
|
|
90
|
+
.update(schema.asset)
|
|
91
|
+
.set({ mime: input.mime, name: input.name, path: file, size: bytes.byteLength })
|
|
92
|
+
.where(eq(schema.asset.id, id));
|
|
93
|
+
await objects.remove(assetKey(row.path));
|
|
94
|
+
}
|
|
80
95
|
/** Deletes one asset: its file and its row. */
|
|
81
96
|
export async function removeAsset(id) {
|
|
82
97
|
const row = await db.query.asset.findFirst({ where: eq(schema.asset.id, id) });
|
|
@@ -600,6 +600,44 @@ export async function embeddingModel() {
|
|
|
600
600
|
? { model: gatewayClient(access, {}).embeddingModel("wizards/embedding"), ref: bound.model }
|
|
601
601
|
: null;
|
|
602
602
|
}
|
|
603
|
+
/**
|
|
604
|
+
* The way to TypeSafe's Jev, a classifier that answers typed questions with probabilities and
|
|
605
|
+
* writes no text: the credits on a Manage-App's runtime; alone, an AI Gateway key, a TypeSafe
|
|
606
|
+
* key, or the linked account's credits. Null when there is none.
|
|
607
|
+
*/
|
|
608
|
+
export async function systemOneAccess() {
|
|
609
|
+
const cfg = await config();
|
|
610
|
+
const viaCredits = (access) => ({
|
|
611
|
+
url: `${access.baseUrl}/v4/ai/systemone`,
|
|
612
|
+
headers: {
|
|
613
|
+
authorization: `Bearer ${access.token}`,
|
|
614
|
+
// Booked under the classifier class; its evaluation model answers.
|
|
615
|
+
"ai-model-id": "wizards/classifier",
|
|
616
|
+
...(access.tenant ? { "x-wizards-tenant": access.tenant } : {}),
|
|
617
|
+
},
|
|
618
|
+
model: null,
|
|
619
|
+
});
|
|
620
|
+
if (managed) {
|
|
621
|
+
const access = await creditAccess(cfg, true).catch(() => null);
|
|
622
|
+
return access ? viaCredits(access) : null;
|
|
623
|
+
}
|
|
624
|
+
if (cfg.keys.gateway) {
|
|
625
|
+
return {
|
|
626
|
+
url: "https://ai-gateway.vercel.sh/typesafe/v1/systemone",
|
|
627
|
+
headers: { authorization: `Bearer ${cfg.keys.gateway}` },
|
|
628
|
+
model: "typesafe-ai/jev",
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
if (env.typesafeKey) {
|
|
632
|
+
return {
|
|
633
|
+
url: "https://api.typesafe.ai/v1/systemone",
|
|
634
|
+
headers: { authorization: `Bearer ${env.typesafeKey}` },
|
|
635
|
+
model: "jev-latest",
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
const access = await creditAccess(cfg, false).catch(() => null);
|
|
639
|
+
return access ? viaCredits(access) : null;
|
|
640
|
+
}
|
|
603
641
|
/** Search tools an AI Gateway runs itself; they travel in the request, so any gateway client names them. */
|
|
604
642
|
export const gatewayTools = createGateway({ apiKey: "unused" }).tools;
|
|
605
643
|
/**
|
|
@@ -11,6 +11,7 @@ import { parseDocument } from "../documents/parse.js";
|
|
|
11
11
|
import { env } from "../env.js";
|
|
12
12
|
import { ModelUnavailableError, textModel } from "../models.js";
|
|
13
13
|
import { putIndexEntry, removeIndexEntries } from "../services/project-index.js";
|
|
14
|
+
import { originData } from "../services/space-origin.js";
|
|
14
15
|
import { safeFetch } from "../tools/net-guard.js";
|
|
15
16
|
import { discoverPlugins } from "./discovery.js";
|
|
16
17
|
import { migrateOpenTenants, migratePlugins } from "./migrations.js";
|
|
@@ -112,6 +113,7 @@ function apiFor(record) {
|
|
|
112
113
|
put: (entry) => putIndexEntry(source.id, entry),
|
|
113
114
|
remove: (space, key) => removeIndexEntries(source.id, space, key),
|
|
114
115
|
},
|
|
116
|
+
spaceData: originData(source.id),
|
|
115
117
|
web: {
|
|
116
118
|
fetch: (url, init) => safeFetch(url, init),
|
|
117
119
|
read: (url, options) => readPage(url, options?.signal),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { MODEL_CLASSES, TEXT_CLASSES } from "@engenty-wizards/shared/definition";
|
|
4
|
+
import { CATEGORY_TYPES, whereSchema } from "@engenty-wizards/shared/knowledge";
|
|
4
5
|
import { PROJECT_FILE_KINDS } from "@engenty-wizards/shared/projects";
|
|
5
6
|
import { isCreditsRef } from "@engenty-wizards/shared/providers";
|
|
6
7
|
import { generateText } from "ai";
|
|
@@ -30,15 +31,18 @@ import { HTML_RESPONSE_CSP } from "../render/guard.js";
|
|
|
30
31
|
import { mayBuild, projectWritable, requireWritable } from "../services/access.js";
|
|
31
32
|
import { architectTurn } from "../services/architect.js";
|
|
32
33
|
import { checkForCloud, cloudLinked, cloudSpaces, cloudState, forgetCloud, publishAndSync, pushSharing, removeFromCloud, replaceCloudSpaces, rotateInCloud, syncToCloud, } from "../services/cloud.js";
|
|
33
|
-
import { ServiceError } from "../services/errors.js";
|
|
34
|
+
import { notFound, ServiceError } from "../services/errors.js";
|
|
34
35
|
import { deleteFile, listFiles, readFile, writeFile } from "../services/files.js";
|
|
36
|
+
import { categoryContents, listKnowledge, resolvePath, valueContents, } from "../services/knowledge.js";
|
|
37
|
+
import { writeSummary } from "../services/knowledge-model.js";
|
|
35
38
|
import { exportWizard, importWizard, PACKAGE_EXTENSION, PACKAGE_MAX_BYTES, PACKAGE_MIME, } from "../services/package.js";
|
|
36
39
|
import { profileSchema, saveProfile, userProfile } from "../services/profile.js";
|
|
37
40
|
import { addProjectFile, fileView, orderProjectFiles, projectFile, projectFileContent, projectFiles, reindexProjectFile, removeProjectFile, updateProjectFile, } from "../services/project-files.js";
|
|
38
|
-
import { documentText,
|
|
41
|
+
import { documentText, queueIndex, searchKnowledge } from "../services/project-index.js";
|
|
39
42
|
import { createProject, deleteProject, listProjects, maskedServers, ownedProject, projectLimit, projectPatchSchema, updateProject, } from "../services/projects.js";
|
|
40
43
|
import { listResults, listRuns, startTestRun } from "../services/runs.js";
|
|
41
|
-
import {
|
|
44
|
+
import { addCategoryValue, categoryRow, categoryValue, deleteCategory, deleteCategoryValue, mergeCategoryValues, orderCategoryValues, putCategory, renameCategoryValue, setItemCategory, updateCategory, } from "../services/space-categories.js";
|
|
45
|
+
import { addTableRow, createPage, createTable, deletePage, deleteTable, deleteTableRows, getPage, getTable, listSpaceData, orderSubPages, updatePage, updateTable, updateTableRow, } from "../services/space-data.js";
|
|
42
46
|
import { draftWidgetPreview } from "../services/widgets.js";
|
|
43
47
|
import { createWizard, deleteWizard, duplicateWizard, listWizards, ownedWizard, rotateShareLink, updateWizardSettings, wizardMessages, wizardState, writableWizard, writeDraft, } from "../services/wizards.js";
|
|
44
48
|
import { readSetting, writeSetting } from "../settings.js";
|
|
@@ -344,11 +348,13 @@ export const studio = new Hono()
|
|
|
344
348
|
const text = await documentText(row);
|
|
345
349
|
return c.json({ text: text.slice(0, limit), chars: text.length });
|
|
346
350
|
})
|
|
347
|
-
// What
|
|
351
|
+
// What a search of Wissen finds for a question: the items an agent step would get.
|
|
348
352
|
.post("/projects/:id/search", async (c) => {
|
|
349
353
|
const project = await ownedProject(c.get("user").id, c.req.param("id"));
|
|
350
|
-
const
|
|
351
|
-
|
|
354
|
+
const body = z
|
|
355
|
+
.object({ query: z.string().min(1).max(400), where: whereSchema.optional() })
|
|
356
|
+
.parse(await c.req.json());
|
|
357
|
+
return c.json(await searchKnowledge(project.id, body.query, { where: body.where }));
|
|
352
358
|
})
|
|
353
359
|
// The assistant fills the project in: from a description, a website, the files given.
|
|
354
360
|
.post("/projects/:id/assist", async (c) => {
|
|
@@ -362,6 +368,7 @@ export const studio = new Hono()
|
|
|
362
368
|
.array(z.object({ role: z.enum(["user", "assistant"]), content: z.string().max(8000) }))
|
|
363
369
|
.max(24)
|
|
364
370
|
.default([]),
|
|
371
|
+
part: z.enum(["info", "knowledge"]).default("info"),
|
|
365
372
|
})
|
|
366
373
|
.parse(await c.req.json());
|
|
367
374
|
if (!(await canSpend())) {
|
|
@@ -376,6 +383,7 @@ export const studio = new Hono()
|
|
|
376
383
|
projectId: project.id,
|
|
377
384
|
message: body.message,
|
|
378
385
|
history: body.history,
|
|
386
|
+
part: body.part,
|
|
379
387
|
signal: abort.signal,
|
|
380
388
|
onText: (delta) => {
|
|
381
389
|
void stream.writeSSE({ event: "text", data: JSON.stringify(delta) });
|
|
@@ -466,6 +474,128 @@ export const studio = new Hono()
|
|
|
466
474
|
mode: query.mode,
|
|
467
475
|
days: query.days,
|
|
468
476
|
}));
|
|
477
|
+
})
|
|
478
|
+
// --- Wissen: its items and Kategorien ---------------------------------------
|
|
479
|
+
.get("/projects/:id/knowledge", async (c) => c.json(await listKnowledge(c.get("user").id, c.req.param("id"))))
|
|
480
|
+
// What a link in a page names (`pages/bgb/p-281`): the studio opens it.
|
|
481
|
+
.get("/projects/:id/resolve", async (c) => {
|
|
482
|
+
const project = await ownedProject(c.get("user").id, c.req.param("id"));
|
|
483
|
+
const found = await resolvePath(project.id, z.string().max(600).parse(c.req.query("path")));
|
|
484
|
+
if (!found) {
|
|
485
|
+
throw notFound();
|
|
486
|
+
}
|
|
487
|
+
return c.json(found);
|
|
488
|
+
})
|
|
489
|
+
.post("/projects/:id/categories", async (c) => {
|
|
490
|
+
const project = await ownedProject(c.get("user").id, c.req.param("id"));
|
|
491
|
+
await requireWritable(project);
|
|
492
|
+
const body = z
|
|
493
|
+
.object({
|
|
494
|
+
name: z.string().min(1).max(80),
|
|
495
|
+
type: z.enum(CATEGORY_TYPES),
|
|
496
|
+
unit: z.string().max(20).nullish(),
|
|
497
|
+
multiple: z.boolean().optional(),
|
|
498
|
+
ordered: z.boolean().optional(),
|
|
499
|
+
hint: z.string().max(120).nullish(),
|
|
500
|
+
values: z.array(z.string().max(200)).max(200).optional(),
|
|
501
|
+
tableId: z.string().nullish(),
|
|
502
|
+
columnId: z.string().nullish(),
|
|
503
|
+
})
|
|
504
|
+
.parse(await c.req.json());
|
|
505
|
+
const row = await putCategory(project.id, body);
|
|
506
|
+
return c.json({ id: row.id });
|
|
507
|
+
})
|
|
508
|
+
.patch("/categories/:id", async (c) => {
|
|
509
|
+
const row = await categoryRow(c.req.param("id"));
|
|
510
|
+
await ownedProject(c.get("user").id, row.projectId);
|
|
511
|
+
const body = z
|
|
512
|
+
.object({
|
|
513
|
+
name: z.string().min(1).max(80).optional(),
|
|
514
|
+
unit: z.string().max(20).nullish(),
|
|
515
|
+
multiple: z.boolean().optional(),
|
|
516
|
+
ordered: z.boolean().optional(),
|
|
517
|
+
hint: z.string().max(120).nullish(),
|
|
518
|
+
proposed: z.literal(false).optional(),
|
|
519
|
+
position: z.number().int().min(0).optional(),
|
|
520
|
+
})
|
|
521
|
+
.parse(await c.req.json());
|
|
522
|
+
await updateCategory(row.id, body);
|
|
523
|
+
return c.json({ ok: true });
|
|
524
|
+
})
|
|
525
|
+
.delete("/categories/:id", async (c) => {
|
|
526
|
+
const row = await categoryRow(c.req.param("id"));
|
|
527
|
+
await ownedProject(c.get("user").id, row.projectId);
|
|
528
|
+
await deleteCategory(row.id);
|
|
529
|
+
return c.json({ ok: true });
|
|
530
|
+
})
|
|
531
|
+
.post("/categories/:id/values", async (c) => {
|
|
532
|
+
const row = await categoryRow(c.req.param("id"));
|
|
533
|
+
await ownedProject(c.get("user").id, row.projectId);
|
|
534
|
+
const { value } = z.object({ value: z.string().min(1).max(200) }).parse(await c.req.json());
|
|
535
|
+
return c.json(await addCategoryValue(row.id, value));
|
|
536
|
+
})
|
|
537
|
+
.put("/categories/:id/values/order", async (c) => {
|
|
538
|
+
const row = await categoryRow(c.req.param("id"));
|
|
539
|
+
await ownedProject(c.get("user").id, row.projectId);
|
|
540
|
+
const { ids } = z.object({ ids: z.array(z.string()).max(500) }).parse(await c.req.json());
|
|
541
|
+
await orderCategoryValues(row.id, ids);
|
|
542
|
+
return c.json({ ok: true });
|
|
543
|
+
})
|
|
544
|
+
.get("/values/:id", async (c) => c.json(await valueContents(c.get("user").id, c.req.param("id"))))
|
|
545
|
+
// Everything that has a Kategorie: the entries its page lists.
|
|
546
|
+
.get("/categories/:id/items", async (c) => c.json(await categoryContents(c.get("user").id, c.req.param("id"))))
|
|
547
|
+
.patch("/values/:id", async (c) => {
|
|
548
|
+
const { category } = await categoryValue(c.req.param("id"));
|
|
549
|
+
await ownedProject(c.get("user").id, category.projectId);
|
|
550
|
+
const { value } = z.object({ value: z.string().min(1).max(200) }).parse(await c.req.json());
|
|
551
|
+
await renameCategoryValue(c.req.param("id"), value);
|
|
552
|
+
return c.json({ ok: true });
|
|
553
|
+
})
|
|
554
|
+
.post("/values/:id/merge", async (c) => {
|
|
555
|
+
const { category } = await categoryValue(c.req.param("id"));
|
|
556
|
+
await ownedProject(c.get("user").id, category.projectId);
|
|
557
|
+
const { into } = z.object({ into: z.string() }).parse(await c.req.json());
|
|
558
|
+
await mergeCategoryValues(c.req.param("id"), into);
|
|
559
|
+
return c.json({ ok: true });
|
|
560
|
+
})
|
|
561
|
+
.delete("/values/:id", async (c) => {
|
|
562
|
+
const { category } = await categoryValue(c.req.param("id"));
|
|
563
|
+
await ownedProject(c.get("user").id, category.projectId);
|
|
564
|
+
await deleteCategoryValue(c.req.param("id"));
|
|
565
|
+
return c.json({ ok: true });
|
|
566
|
+
})
|
|
567
|
+
// The Übersicht of a value: a model writes it from the value's items.
|
|
568
|
+
.post("/values/:id/summary", async (c) => {
|
|
569
|
+
const { category } = await categoryValue(c.req.param("id"));
|
|
570
|
+
const project = await ownedProject(c.get("user").id, category.projectId);
|
|
571
|
+
await requireWritable(project);
|
|
572
|
+
return c.json({ summary: await writeSummary(c.req.param("id")) });
|
|
573
|
+
})
|
|
574
|
+
// A Kategorie a person sets on an item: `p:<page>`, `t:<table>`, `r:<row>`, `f:<file>`.
|
|
575
|
+
.put("/projects/:id/items/:key/categories/:categoryId", async (c) => {
|
|
576
|
+
const project = await ownedProject(c.get("user").id, c.req.param("id"));
|
|
577
|
+
await requireWritable(project);
|
|
578
|
+
const key = c.req.param("key");
|
|
579
|
+
const id = key.slice(2);
|
|
580
|
+
const ref = key[0] === "p"
|
|
581
|
+
? { pageId: id }
|
|
582
|
+
: key[0] === "t"
|
|
583
|
+
? { tableId: id }
|
|
584
|
+
: key[0] === "r"
|
|
585
|
+
? { rowId: id }
|
|
586
|
+
: key[0] === "f"
|
|
587
|
+
? { fileId: id }
|
|
588
|
+
: null;
|
|
589
|
+
if (!ref || key[1] !== ":") {
|
|
590
|
+
throw new ServiceError("invalid", "Unbekannter Eintrag.");
|
|
591
|
+
}
|
|
592
|
+
const { values } = z
|
|
593
|
+
.object({ values: z.array(z.union([z.string().max(300), z.number(), z.boolean()])).max(50) })
|
|
594
|
+
.parse(await c.req.json());
|
|
595
|
+
if (await setItemCategory(project.id, ref, c.req.param("categoryId"), values)) {
|
|
596
|
+
queueIndex(key[0] === "p" ? `P:${id}` : key);
|
|
597
|
+
}
|
|
598
|
+
return c.json({ ok: true });
|
|
469
599
|
})
|
|
470
600
|
// --- the space's tables and pages --------------------------------------------
|
|
471
601
|
.get("/projects/:id/data", async (c) => c.json(await listSpaceData(c.get("user").id, c.req.param("id"))))
|
|
@@ -475,6 +605,7 @@ export const studio = new Hono()
|
|
|
475
605
|
title: z.string().max(120),
|
|
476
606
|
wizardId: z.string().nullish(),
|
|
477
607
|
columns: z.unknown().optional(),
|
|
608
|
+
format: z.enum(["faq"]).nullish(),
|
|
478
609
|
})
|
|
479
610
|
.parse(await c.req.json());
|
|
480
611
|
return c.json(await createTable(c.get("user").id, c.req.param("id"), body));
|
|
@@ -486,6 +617,8 @@ export const studio = new Hono()
|
|
|
486
617
|
title: z.string().max(120).optional(),
|
|
487
618
|
wizardId: z.string().nullish(),
|
|
488
619
|
columns: z.unknown().optional(),
|
|
620
|
+
format: z.enum(["faq"]).nullish(),
|
|
621
|
+
review: z.null().optional(),
|
|
489
622
|
})
|
|
490
623
|
.parse(await c.req.json());
|
|
491
624
|
await updateTable(c.get("user").id, c.req.param("id"), body);
|
|
@@ -515,6 +648,7 @@ export const studio = new Hono()
|
|
|
515
648
|
.object({
|
|
516
649
|
title: z.string().max(200),
|
|
517
650
|
wizardId: z.string().nullish(),
|
|
651
|
+
parentId: z.string().nullish(),
|
|
518
652
|
markdown: z.string().optional(),
|
|
519
653
|
})
|
|
520
654
|
.parse(await c.req.json());
|
|
@@ -526,11 +660,18 @@ export const studio = new Hono()
|
|
|
526
660
|
.object({
|
|
527
661
|
title: z.string().max(200).optional(),
|
|
528
662
|
wizardId: z.string().nullish(),
|
|
663
|
+
parentId: z.string().nullish(),
|
|
529
664
|
markdown: z.string().optional(),
|
|
665
|
+
review: z.null().optional(),
|
|
530
666
|
})
|
|
531
667
|
.parse(await c.req.json());
|
|
532
668
|
await updatePage(c.get("user").id, c.req.param("id"), body);
|
|
533
669
|
return c.json({ ok: true });
|
|
670
|
+
})
|
|
671
|
+
.put("/pages/:id/order", async (c) => {
|
|
672
|
+
const { ids } = z.object({ ids: z.array(z.string()).max(5000) }).parse(await c.req.json());
|
|
673
|
+
await orderSubPages(c.get("user").id, c.req.param("id"), ids);
|
|
674
|
+
return c.json({ ok: true });
|
|
534
675
|
})
|
|
535
676
|
.delete("/pages/:id", async (c) => {
|
|
536
677
|
await deletePage(c.get("user").id, c.req.param("id"));
|