wizards 0.6.1 → 0.7.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.
- package/README.md +6 -2
- package/apps/runtime/dist/agents/architect.js +7 -29
- package/apps/runtime/dist/agents/studio-assistant.js +7 -24
- package/apps/runtime/dist/authoring/guide.js +1 -1
- package/apps/runtime/dist/cli/autostart.js +58 -12
- package/apps/runtime/dist/cli/autoupdate.js +221 -0
- package/apps/runtime/dist/cli/environment.js +5 -0
- package/apps/runtime/dist/cli/main.js +9 -2
- package/apps/runtime/dist/cli/menu.js +2 -2
- package/apps/runtime/dist/cli/processes.js +72 -0
- package/apps/runtime/dist/cli/setup.js +9 -4
- package/apps/runtime/dist/cli/start.js +18 -1
- package/apps/runtime/dist/cli/status.js +51 -17
- package/apps/runtime/dist/cli/uninstall-main.js +20 -0
- package/apps/runtime/dist/cli/uninstall.js +231 -0
- package/apps/runtime/dist/cli/update.js +25 -2
- package/apps/runtime/dist/cli/versions.js +106 -0
- package/apps/runtime/dist/docs/dev/wizard-format.md +6 -4
- package/apps/runtime/dist/docs/user/command-line.md +2 -1
- package/apps/runtime/dist/docs/user/data.md +1 -1
- package/apps/runtime/dist/docs/user/install.md +29 -6
- package/apps/runtime/dist/engine/input.js +4 -5
- package/apps/runtime/dist/engine/runner.js +5 -5
- package/apps/runtime/dist/engine/steps.js +5 -5
- package/apps/runtime/dist/engine/template.js +10 -15
- package/apps/runtime/dist/env.js +3 -20
- package/apps/runtime/dist/features.js +2 -2
- package/apps/runtime/dist/index.js +3 -1
- package/apps/runtime/dist/manage.js +7 -0
- package/apps/runtime/dist/media/conversation.js +2 -2
- package/apps/runtime/dist/models.js +12 -0
- package/apps/runtime/dist/plugins/runs.js +4 -4
- package/apps/runtime/dist/routes/runs.js +4 -2
- package/apps/runtime/dist/sandbox/docker.js +0 -2
- package/apps/runtime/dist/sandbox/index.js +3 -10
- package/apps/runtime/dist/services/api.js +2 -2
- package/apps/runtime/dist/services/runners.js +2 -2
- package/apps/runtime/dist/services/space-categories.js +3 -25
- package/apps/runtime/dist/services/translations.js +7 -4
- package/apps/runtime/dist/tools/browser.js +2 -14
- package/apps/runtime/dist/tools/index.js +1 -19
- package/apps/runtime/dist/tools/mail.js +1 -4
- package/apps/runtime/dist/tools/shared.js +30 -3
- package/apps/runtime/dist/update.js +10 -19
- package/apps/web/dist/assets/AboutDialog-CUVmdhzk.js +1 -0
- package/apps/web/dist/assets/{ChangelogDialog-jQk_bQpP.js → ChangelogDialog-DcQqFbiW.js} +1 -1
- package/apps/web/dist/assets/ChatView-DTPWjzWF.js +10 -0
- package/apps/web/dist/assets/EditorPage-SJHtft7P.js +3 -0
- package/apps/web/dist/assets/{FlowPreview-DA0sHtr7.js → FlowPreview-DvpjuQdC.js} +1 -1
- package/apps/web/dist/assets/{Harness-DmYkwU4x.js → Harness-D6fezoWO.js} +2 -2
- package/apps/web/dist/assets/{NewWizardChannels-BLS9sKhT.js → NewWizardChannels-BHeutZ7V.js} +1 -1
- package/apps/web/dist/assets/NewWizardPage-CzBxaPhH.js +7 -0
- package/apps/web/dist/assets/{OnboardingPage-CREvCCdX.js → OnboardingPage-DbR4N2Ms.js} +1 -1
- package/apps/web/dist/assets/ProjectPage-DG6_E7kA.js +1 -0
- package/apps/web/dist/assets/PublicRunner-19tV_8TR.js +1 -0
- package/apps/web/dist/assets/{ReadOnly-b7TjjRAk.js → ReadOnly-BMXFtKs0.js} +1 -1
- package/apps/web/dist/assets/RunDrawer-DqwLjFJb.js +4 -0
- package/apps/web/dist/assets/{RunnerView-CLfZzbKn.js → RunnerView-DRc4J-rg.js} +1 -1
- package/apps/web/dist/assets/SettingsPage-CCypPS9L.js +1 -0
- package/apps/web/dist/assets/SetupPage-D2Y2rev4.js +1 -0
- package/apps/web/dist/assets/{SharePage-B21QI97K.js → SharePage-DB5n6Oug.js} +1 -1
- package/apps/web/dist/assets/changelog-BDhd7tjO.js +1 -0
- package/apps/web/dist/assets/{esm-DqRvG-PU.js → esm-6TNBQNCD.js} +1 -1
- package/apps/web/dist/assets/index-mBRI7ZQp.js +14 -0
- package/apps/web/dist/assets/{lock-D_KjsGfU.js → lock-HLZib0u9.js} +1 -1
- package/apps/web/dist/assets/{phone-YRxkPQk6.js → phone-DsxbIcWi.js} +1 -1
- package/apps/web/dist/assets/settings-menu-BMMkIVP1.js +1 -0
- package/apps/web/dist/assets/ui-Brg2qM1A.js +137 -0
- package/apps/web/dist/assets/{upload-DDo14e2p.js → upload-BaGy0ppG.js} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/apps/web/dist/install.sh +29 -11
- package/apps/web/dist/mcp/flow.html +11 -11
- package/bin/wizards.mjs +17 -3
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +18 -7
- package/node_modules/@engenty-wizards/shared/dist/definition.js +50 -21
- package/node_modules/@engenty-wizards/shared/dist/features.d.ts +6 -0
- package/node_modules/@engenty-wizards/shared/dist/features.js +12 -0
- package/node_modules/@engenty-wizards/shared/dist/numbers.d.ts +24 -0
- package/node_modules/@engenty-wizards/shared/dist/numbers.js +54 -0
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +4 -4
- package/node_modules/@engenty-wizards/shared/dist/store.d.ts +7 -0
- package/node_modules/@engenty-wizards/shared/dist/store.js +28 -0
- package/package.json +1 -1
- package/apps/runtime/dist/sandbox/agentos.js +0 -191
- package/apps/web/dist/assets/AboutDialog-CTuI-l3p.js +0 -1
- package/apps/web/dist/assets/ChatView-9TLi0B7K.js +0 -10
- package/apps/web/dist/assets/EditorPage-BVrzOfF3.js +0 -3
- package/apps/web/dist/assets/NewWizardPage-DWYZYiBF.js +0 -7
- package/apps/web/dist/assets/ProjectPage-BQkd55xR.js +0 -1
- package/apps/web/dist/assets/PublicRunner-sOlOC0zD.js +0 -1
- package/apps/web/dist/assets/RunDrawer-BPIVaan7.js +0 -4
- package/apps/web/dist/assets/SettingsPage-BO6TPmoj.js +0 -1
- package/apps/web/dist/assets/SetupPage-DlJHBYWH.js +0 -1
- package/apps/web/dist/assets/changelog-DeSoCsai.js +0 -1
- package/apps/web/dist/assets/index-CAUeJXCg.js +0 -14
- package/apps/web/dist/assets/settings-menu-CQgehwEX.js +0 -1
- package/apps/web/dist/assets/ui-DDUCc_4v.js +0 -137
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: The wizard format
|
|
3
3
|
description: A wizard as JSON and as a .wizard file, its JSON Schema, and checking it with wizards validate.
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.13
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
A wizard is one JSON document, its **definition**. The studio exports it as a `.wizard` file
|
|
@@ -30,9 +30,11 @@ tool may read and write wizards.
|
|
|
30
30
|
lists the step types and their fields. The studio’s MCP tool `get_authoring_guide` explains them
|
|
31
31
|
with a complete example.
|
|
32
32
|
|
|
33
|
-
`lang` is the language the wizard speaks to people
|
|
34
|
-
and
|
|
35
|
-
|
|
33
|
+
`lang` is the language the wizard speaks to people, as a two-letter code: `en`, `de`, `it`, `fr`
|
|
34
|
+
and so on. The schema checks the shape of the code; `wizards validate` and the studio check that
|
|
35
|
+
it names a real language. It sets the runner’s own words and how numbers and dates are written.
|
|
36
|
+
Without it, the language is read from the wizard’s own words, and English is the default where
|
|
37
|
+
they are not clearly German.
|
|
36
38
|
|
|
37
39
|
`version` is the definition’s version. It changes only with a breaking change; the schemas of
|
|
38
40
|
each version stay online in their own folder (`/schema/v1/`, `/schema/v2/`, …).
|
|
@@ -18,13 +18,14 @@ version: 0.5.0
|
|
|
18
18
|
| `wizards doctor` | Shows the status and checks for problems |
|
|
19
19
|
| `wizards logs [n]` | Prints the last lines engenty wizards printed while running (100, or `n` up to 2000). Keys and tokens are taken out |
|
|
20
20
|
| `wizards report` | Sends the status, the model settings (never a key) and the last 500 log lines to your [account](./models.md#account), so support can see what went wrong. Nothing is sent unless you run it. The command names the report’s ID. If this computer isn’t signed in, it tells you to sign in under Settings → Account or with `wizards account sign-in` |
|
|
21
|
-
| `wizards stop` | Stops it. Your data is kept |
|
|
21
|
+
| `wizards stop` | Stops it, with any other copy of it still running in the background. Your data is kept. AI apps that use your wizards start it again when you use one there |
|
|
22
22
|
| `wizards restart` | Stops it and starts it again; after an update, that is the new version. If it isn’t running, it starts in the background |
|
|
23
23
|
| `wizards update` | Installs the latest version |
|
|
24
24
|
| `wizards autostart on` / `off` | Starts it when you log in, or stops doing so. It is a LaunchAgent on macOS and a systemd user service on Linux |
|
|
25
25
|
| `wizards connect [app …]` | Adds your wizards to your [AI apps](./ai-apps.md): every app found, or the ones you name |
|
|
26
26
|
| `wizards disconnect [app …]` | Takes them out again |
|
|
27
27
|
| `wizards mcp` | Runs the MCP server over stdio. AI apps start it; you don’t |
|
|
28
|
+
| `wizards uninstall` | Takes engenty wizards off this computer. It shows what it will remove first and asks about your data on its own; your data is kept unless you choose to delete it. `--dry-run` only shows the plan, `--yes` asks nothing and keeps your data, `--yes --delete-data` deletes it too. See [Remove](./install.md#remove) |
|
|
28
29
|
| `wizards validate <path …>` | Checks wizards without the studio: `.wizard` files, unpacked wizard files, or a wizard’s JSON. `--json` prints the problems as JSON. A space’s connectors and plugins are checked when you import it |
|
|
29
30
|
|
|
30
31
|
| Option | What it does |
|
|
@@ -27,7 +27,7 @@ Keychain (as `engenty-wizards`). On other systems they are stored encrypted in t
|
|
|
27
27
|
|---|---|
|
|
28
28
|
| Back up | Stop engenty wizards (`wizards stop`) and copy the folder |
|
|
29
29
|
| Move to another computer | Install engenty wizards there, stop it and put the folder in place. On a Mac, enter your API keys and sign in to your account again: the Keychain doesn’t move |
|
|
30
|
-
| Start over |
|
|
30
|
+
| Start over | Run `wizards uninstall`, choose to delete your data, then install again. On a Mac, the keys in the Keychain stay and are used again. To remove them, delete the `engenty-wizards` entries in Keychain Access |
|
|
31
31
|
| Keep it elsewhere | Set `ENGENTY_HOME` to another folder. engenty wizards then keeps everything in its `wizards` folder there |
|
|
32
32
|
|
|
33
33
|
After an update, engenty wizards brings your data up to date the next time it starts.
|
|
@@ -71,6 +71,19 @@ the right of the address bar.
|
|
|
71
71
|
|
|
72
72
|
## Update
|
|
73
73
|
|
|
74
|
+
engenty wizards updates itself: it looks for a new version when it starts and once a day,
|
|
75
|
+
downloads it in the background and checks it, and switches to it at the next start (a restart or
|
|
76
|
+
the next login), never while a wizard runs. The version before stays beside it. `wizards status`
|
|
77
|
+
and the studio then say “Updated to …” with what changed. To turn it off:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
wizards autoupdate off
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
or set `WIZARDS_DISABLE_AUTOUPDATE=1` (in the terminal or in `~/.engenty/wizards/.env`). In CI,
|
|
84
|
+
and for an install set up without a terminal, it is off unless you turn it on with `wizards
|
|
85
|
+
autoupdate on`. `npx wizards` never updates itself. To update now:
|
|
86
|
+
|
|
74
87
|
```bash
|
|
75
88
|
wizards update
|
|
76
89
|
```
|
|
@@ -88,11 +101,21 @@ brought up to date at the next start.
|
|
|
88
101
|
## Remove
|
|
89
102
|
|
|
90
103
|
```bash
|
|
91
|
-
wizards
|
|
92
|
-
wizards autostart off
|
|
93
|
-
wizards disconnect
|
|
104
|
+
wizards uninstall
|
|
94
105
|
```
|
|
95
106
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
First it shows what it will remove: your wizards in your AI apps, starting at login, everything of
|
|
108
|
+
engenty wizards that is running, the program in `~/.engenty/wizards` and the command
|
|
109
|
+
`~/.local/bin/wizards`. Your data in `~/.engenty/wizards/data` is kept unless you say to delete
|
|
110
|
+
it, and installing again picks it up. [Back it up](./data.md) first if you delete it. At the end
|
|
111
|
+
it lists anything it couldn’t remove.
|
|
112
|
+
|
|
113
|
+
If the `wizards` command no longer runs, `npx wizards uninstall` does the same.
|
|
114
|
+
|
|
115
|
+
| Option | What it does |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `--dry-run` | Only shows what it would remove |
|
|
118
|
+
| `--yes` | Removes everything without asking and keeps your data |
|
|
119
|
+
| `--yes --delete-data` | Deletes your data as well |
|
|
120
|
+
|
|
121
|
+
Without a terminal, it asks for `--yes`.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDecidedField, isLang, isLocationValue, MAX_FILES, offeredSlot, shownFields, } from "@engenty-wizards/shared/definition";
|
|
2
|
+
import { readNumber } from "@engenty-wizards/shared/numbers";
|
|
2
3
|
import { note, saidText } from "@engenty-wizards/shared/run";
|
|
3
4
|
/** The field errors in a language, for whoever reads them. */
|
|
4
5
|
export function fieldErrorsIn(errors, lang) {
|
|
@@ -13,11 +14,10 @@ function coerce(field, raw) {
|
|
|
13
14
|
}
|
|
14
15
|
switch (field.kind) {
|
|
15
16
|
case "number": {
|
|
16
|
-
if (raw === "") {
|
|
17
|
+
if (String(raw).trim() === "") {
|
|
17
18
|
return undefined;
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
-
return Number.isFinite(n) ? n : Number.NaN;
|
|
20
|
+
return readNumber(raw) ?? Number.NaN;
|
|
21
21
|
}
|
|
22
22
|
case "toggle":
|
|
23
23
|
return raw === true || raw === "true" || raw === "on";
|
|
@@ -62,8 +62,7 @@ function coerce(field, raw) {
|
|
|
62
62
|
if (c.kind === "text") {
|
|
63
63
|
return [c.id, String(v ?? "").slice(0, 2000)];
|
|
64
64
|
}
|
|
65
|
-
|
|
66
|
-
return [c.id, Number.isFinite(n) ? n : 0];
|
|
65
|
+
return [c.id, readNumber(v) ?? 0];
|
|
67
66
|
})))
|
|
68
67
|
.filter((r) => Object.values(r).some((v) => v !== "" && v !== 0))
|
|
69
68
|
.slice(0, 200);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { marginOf, } from "@engenty-wizards/shared/decision";
|
|
2
|
-
import { branchValues, cardsFromData, conditionsOf, dataRef, followRules, formatsFor, isDecidedField, LIST_FORMATS, listRef, loopKey, MAX_SHOWN_FIELDS, optionsFromData, otherwiseOf, pageNeeds, whenHolds, wizardLang, } from "@engenty-wizards/shared/definition";
|
|
2
|
+
import { branchValues, cardsFromData, conditionsOf, dataRef, followRules, formatsFor, isDecidedField, LIST_FORMATS, listRef, loopKey, MAX_SHOWN_FIELDS, optionsFromData, otherwiseOf, pageNeeds, textLang, whenHolds, wizardLang, } from "@engenty-wizards/shared/definition";
|
|
3
3
|
import { note, saidText, } from "@engenty-wizards/shared/run";
|
|
4
4
|
import { CONVERSATION_RUNNERS, DEFAULT_RUNNER_SETTINGS } from "@engenty-wizards/shared/runners";
|
|
5
5
|
import { fitData } from "@engenty-wizards/shared/shape";
|
|
@@ -140,7 +140,7 @@ function friendly(err) {
|
|
|
140
140
|
*/
|
|
141
141
|
async function failRun(run, stepId, err) {
|
|
142
142
|
const said = noteOf(err) ?? friendly(err);
|
|
143
|
-
const message = saidText(said, wizardLang(run.definition));
|
|
143
|
+
const message = saidText(said, textLang(wizardLang(run.definition)));
|
|
144
144
|
await emitEvent(run.id, stepId, "error", said);
|
|
145
145
|
await updateRun(run.id, { status: "failed", error: message });
|
|
146
146
|
}
|
|
@@ -229,7 +229,7 @@ async function take(runId, tenant) {
|
|
|
229
229
|
console.error(`[run ${runId}]`, err);
|
|
230
230
|
await withTenant(tenant, async () => {
|
|
231
231
|
const run = await loadRun(runId);
|
|
232
|
-
const lang = run ? wizardLang(run.definition) : "en";
|
|
232
|
+
const lang = run ? textLang(wizardLang(run.definition)) : "en";
|
|
233
233
|
await updateRun(runId, { status: "failed", error: saidText(friendly(err), lang) });
|
|
234
234
|
}).catch((e) => console.error(`[run ${runId}] marking it failed:`, e));
|
|
235
235
|
})
|
|
@@ -655,7 +655,7 @@ export async function submitPage(runId, stepId, input) {
|
|
|
655
655
|
throw new RunInputError(errors);
|
|
656
656
|
}
|
|
657
657
|
const connections = step.fields.some((f) => f.kind === "connection")
|
|
658
|
-
? await connectionViews(run.definition.connections ?? [], scopeOf(run), await projectIdOf(run), wizardLang(run.definition))
|
|
658
|
+
? await connectionViews(run.definition.connections ?? [], scopeOf(run), await projectIdOf(run), textLang(wizardLang(run.definition)))
|
|
659
659
|
: [];
|
|
660
660
|
for (const field of step.fields) {
|
|
661
661
|
if (field.kind === "image" || field.kind === "file") {
|
|
@@ -961,7 +961,7 @@ export async function runView(run, brand) {
|
|
|
961
961
|
ask: run.status === "running" ? run.ask : null,
|
|
962
962
|
lists,
|
|
963
963
|
connections: step?.type === "page" && step.fields.some((f) => f.kind === "connection")
|
|
964
|
-
? await connectionViews(def.connections ?? [], scopeOf(run), await projectIdOf(run), wizardLang(def))
|
|
964
|
+
? await connectionViews(def.connections ?? [], scopeOf(run), await projectIdOf(run), textLang(wizardLang(def)))
|
|
965
965
|
: [],
|
|
966
966
|
keeps: Boolean(def.lists?.length || def.connections?.length),
|
|
967
967
|
shareUrl: run.shareToken ? `${env.publicUrl}/s/${run.shareToken}` : null,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { pOf } from "@engenty-wizards/shared/decision";
|
|
2
|
-
import { allFields, dataRef, isDecisionStep,
|
|
2
|
+
import { allFields, dataRef, isDecisionStep, langNameEn, localeOf, stepShape, templateRefs, wizardLang, } from "@engenty-wizards/shared/definition";
|
|
3
3
|
import { mainLogo, ruleLines, runTheme } from "@engenty-wizards/shared/projects";
|
|
4
4
|
import { note as runNote } from "@engenty-wizards/shared/run";
|
|
5
5
|
import { fitData, labelOf, recordsOf, shapeJsonSchema, shapeLines, shapeZod, titleKey, } from "@engenty-wizards/shared/shape";
|
|
@@ -11,7 +11,7 @@ import { snapshotHtmlImages } from "../files/web-images.js";
|
|
|
11
11
|
import { runFilmStep } from "../film/step.js";
|
|
12
12
|
import { generateImageMedia, generateSpeechMedia, generateVideoMedia, } from "../media/generate.js";
|
|
13
13
|
import { markMedia } from "../media/marking.js";
|
|
14
|
-
import { attachTools, costOf, isHarnessVendor, textModel } from "../models.js";
|
|
14
|
+
import { agentModel, attachTools, costOf, isHarnessVendor, textModel, totalUsage, } from "../models.js";
|
|
15
15
|
import { knowledgeBlock } from "../services/knowledge.js";
|
|
16
16
|
import { buildStepTools } from "../tools/index.js";
|
|
17
17
|
import { callPluginTool, spaceContextOf } from "../tools/plugin.js";
|
|
@@ -24,7 +24,7 @@ import { answersAsText, assetSectionLine, colorLine, factLines, logoSectionLine,
|
|
|
24
24
|
import { StepError } from "./types.js";
|
|
25
25
|
/** Today's date as the wizard's people write it. */
|
|
26
26
|
function today(ctx) {
|
|
27
|
-
return new Date().toLocaleDateString(
|
|
27
|
+
return new Date().toLocaleDateString(localeOf(wizardLang(ctx.def)), {
|
|
28
28
|
year: "numeric",
|
|
29
29
|
month: "long",
|
|
30
30
|
day: "numeric",
|
|
@@ -536,7 +536,7 @@ export async function runAgentStep(step, ctx) {
|
|
|
536
536
|
id: `step-${step.id}`,
|
|
537
537
|
name: step.title,
|
|
538
538
|
instructions: system,
|
|
539
|
-
model: resolved
|
|
539
|
+
model: agentModel(resolved),
|
|
540
540
|
tools,
|
|
541
541
|
});
|
|
542
542
|
// Working through a mailbox or a row of portals takes many small tool calls.
|
|
@@ -564,7 +564,7 @@ export async function runAgentStep(step, ctx) {
|
|
|
564
564
|
const result = step.output.format === "json"
|
|
565
565
|
? await streamWithProgress(agent, message, { maxSteps, abortSignal: ctx.signal }, step, ctx)
|
|
566
566
|
: await agent.generate(message, { maxSteps, abortSignal: ctx.signal });
|
|
567
|
-
await ctx.chargeUsd(costOf(resolved,
|
|
567
|
+
await ctx.chargeUsd(costOf(resolved, await totalUsage(result)));
|
|
568
568
|
// `result.text` strings together what the model said between tool calls ("I open the page …").
|
|
569
569
|
// A written result is its last message; structuring keeps everything, numbers may sit anywhere.
|
|
570
570
|
const closing = String(result.steps?.at?.(-1)?.text ?? "").trim();
|
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
import { allFields, isAudioValue, isLocationValue, itemsTotals,
|
|
1
|
+
import { allFields, isAudioValue, isLocationValue, itemsTotals, localeOf, locationText, wizardLang, } from "@engenty-wizards/shared/definition";
|
|
2
|
+
import { formatMoney } from "@engenty-wizards/shared/numbers";
|
|
2
3
|
import { fontLicenseWords, gradientCss, textStyleLine, } from "@engenty-wizards/shared/projects";
|
|
3
4
|
import { listAsMarkdown } from "@engenty-wizards/shared/store";
|
|
4
5
|
const TEMPLATE_RE = /\{\{\s*([^}]+?)\s*\}\}/g;
|
|
5
|
-
function money(n, currency, locale) {
|
|
6
|
-
try {
|
|
7
|
-
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(n);
|
|
8
|
-
}
|
|
9
|
-
catch {
|
|
10
|
-
return `${n.toFixed(2)} ${currency}`;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
6
|
/** Line items as a Markdown table with their totals — what a model needs to write a document. */
|
|
14
7
|
export function itemsAsMarkdown(field, value, values, lang = "en") {
|
|
15
|
-
const locale =
|
|
8
|
+
const locale = localeOf(lang);
|
|
16
9
|
const totals = itemsTotals(field, value, values);
|
|
17
10
|
const cols = field.columns ?? [];
|
|
18
11
|
if (!totals.rows.length) {
|
|
@@ -21,13 +14,15 @@ export function itemsAsMarkdown(field, value, values, lang = "en") {
|
|
|
21
14
|
const head = `| ${cols.map((c) => c.label).join(" | ")} | Amount |\n|${cols.map(() => " --- ").join("|")}| ---: |`;
|
|
22
15
|
const body = totals.rows.map((r) => `| ${cols
|
|
23
16
|
.map((c) => c.kind === "money"
|
|
24
|
-
?
|
|
17
|
+
? formatMoney(Number(r[c.id]) || 0, totals.currency, locale)
|
|
25
18
|
: String(r[c.id] ?? ""))
|
|
26
|
-
.join(" | ")} | ${
|
|
19
|
+
.join(" | ")} | ${formatMoney(r.amount, totals.currency, locale)} |`);
|
|
27
20
|
const sums = [
|
|
28
|
-
`Net: ${
|
|
29
|
-
totals.vatRate
|
|
30
|
-
|
|
21
|
+
`Net: ${formatMoney(totals.net, totals.currency, locale)}`,
|
|
22
|
+
totals.vatRate
|
|
23
|
+
? `VAT ${totals.vatRate}%: ${formatMoney(totals.vat, totals.currency, locale)}`
|
|
24
|
+
: null,
|
|
25
|
+
`Total: ${formatMoney(totals.gross, totals.currency, locale)}`,
|
|
31
26
|
].filter(Boolean);
|
|
32
27
|
return [head, ...body, "", ...sums].join("\n");
|
|
33
28
|
}
|
package/apps/runtime/dist/env.js
CHANGED
|
@@ -23,8 +23,7 @@ function loadEnvFiles() {
|
|
|
23
23
|
loadEnvFiles();
|
|
24
24
|
const str = (key, fallback = "") => process.env[key]?.trim() || fallback;
|
|
25
25
|
/**
|
|
26
|
-
* Without `SANDBOX`: Docker when the sandbox image is on this machine, else
|
|
27
|
-
* packages are installed (a checkout has them, the image and the desktop app do not), else none.
|
|
26
|
+
* Without `SANDBOX`: Docker when the sandbox image is on this machine, else none.
|
|
28
27
|
* The image is not published, so a fresh install would otherwise offer a tool that cannot start.
|
|
29
28
|
*/
|
|
30
29
|
function sandboxEngine(image) {
|
|
@@ -33,12 +32,9 @@ function sandboxEngine(image) {
|
|
|
33
32
|
return "off";
|
|
34
33
|
}
|
|
35
34
|
if (engine) {
|
|
36
|
-
return engine === "agentos" ? "agentos" : "docker";
|
|
37
|
-
}
|
|
38
|
-
if (dockerHasImage(image)) {
|
|
39
35
|
return "docker";
|
|
40
36
|
}
|
|
41
|
-
return
|
|
37
|
+
return dockerHasImage(image) ? "docker" : "off";
|
|
42
38
|
}
|
|
43
39
|
function dockerHasImage(image) {
|
|
44
40
|
try {
|
|
@@ -49,19 +45,6 @@ function dockerHasImage(image) {
|
|
|
49
45
|
return false;
|
|
50
46
|
}
|
|
51
47
|
}
|
|
52
|
-
/** agentOS ships its sidecar for macOS and Linux on arm64 and x64 only. */
|
|
53
|
-
function agentOsInstalled() {
|
|
54
|
-
if (!["darwin", "linux"].includes(process.platform) || !["arm64", "x64"].includes(process.arch)) {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
try {
|
|
58
|
-
import.meta.resolve("@rivet-dev/agentos-core");
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
48
|
/** A comma-separated list. */
|
|
66
49
|
const list = (key) => str(key)
|
|
67
50
|
.split(",")
|
|
@@ -234,7 +217,7 @@ export const env = {
|
|
|
234
217
|
: "/usr/bin/chromium")),
|
|
235
218
|
/** Encodes widget animations to MP4; without it widgets offer no video. */
|
|
236
219
|
ffmpegPath: str("FFMPEG_PATH", "ffmpeg"),
|
|
237
|
-
/** What runs an agent's shell and code: a Docker container
|
|
220
|
+
/** What runs an agent's shell and code: a Docker container or nothing. */
|
|
238
221
|
sandbox: sandboxEngine(sandboxImage),
|
|
239
222
|
sandboxImage,
|
|
240
223
|
/** Assets and workspace files in an S3-compatible bucket (R2); empty = the data folder. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import { declaredByPlugin, defaultFeatures, FEATURES, parseFeatures, resolveFeatures, } from "@engenty-wizards/shared/features";
|
|
3
|
+
import { declaredByPlugin, defaultFeatures, FEATURES, featureOn, parseFeatures, resolveFeatures, } from "@engenty-wizards/shared/features";
|
|
4
4
|
import { accountOverview } from "./auth/account.js";
|
|
5
5
|
import { isCheckout, layout, packageRoot } from "./cli/home.js";
|
|
6
6
|
import { env } from "./env.js";
|
|
@@ -61,7 +61,7 @@ export function features(remote) {
|
|
|
61
61
|
return resolveFeatures(defaultFeatures(declaredFeatures()), envFeatures(), remote, fileFeatures());
|
|
62
62
|
}
|
|
63
63
|
export function feature(name, remote) {
|
|
64
|
-
return features(remote)
|
|
64
|
+
return featureOn(features(remote), name);
|
|
65
65
|
}
|
|
66
66
|
let accountCache = null;
|
|
67
67
|
/**
|
|
@@ -17,7 +17,7 @@ import { purgeExpiredRuns } from "./services/shares.js";
|
|
|
17
17
|
import { purgeUnusedStores } from "./store/index.js";
|
|
18
18
|
import { forgetRunner, registerRunner } from "./tenants/control.js";
|
|
19
19
|
import { LOCAL_TENANT } from "./tenants/tenant.js";
|
|
20
|
-
import { onRestart, RESTART_EXIT } from "./update.js";
|
|
20
|
+
import { onRestart, RESTART_EXIT, startUpdateChecks } from "./update.js";
|
|
21
21
|
// Before anything prints: the last lines stay in memory for a look from afar (debug.ts).
|
|
22
22
|
hookConsole();
|
|
23
23
|
await migrateControlDb();
|
|
@@ -39,6 +39,8 @@ else {
|
|
|
39
39
|
void loadCatalog();
|
|
40
40
|
void checkModelsAtStart().catch(() => undefined);
|
|
41
41
|
startProChecks();
|
|
42
|
+
// A newer release is downloaded beside this one and used from the next start.
|
|
43
|
+
startUpdateChecks();
|
|
42
44
|
}
|
|
43
45
|
// The marketplace's starters and the starred entries, kept for offline use; checked hourly.
|
|
44
46
|
void syncMarketplace();
|
|
@@ -202,6 +202,13 @@ export async function tenantChannels(id) {
|
|
|
202
202
|
channels.set(id, { at: Date.now(), value });
|
|
203
203
|
return value;
|
|
204
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Forgets what the Manage-App said of the tenant's numbers: the next `tenantChannels` asks.
|
|
207
|
+
* The runtime tests of the Manage-App's channel plugins (sms, calls, whatsapp) call it.
|
|
208
|
+
*/
|
|
209
|
+
export function forgetChannels(id) {
|
|
210
|
+
channels.delete(id);
|
|
211
|
+
}
|
|
205
212
|
/** A person of the tenant asks for its number; what the Manage-App answers with is the request. */
|
|
206
213
|
export async function requestChannelNumber(id, input) {
|
|
207
214
|
channels.delete(id);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { langNameEn } from "@engenty-wizards/shared/definition";
|
|
2
2
|
import { NotedError } from "../model-errors.js";
|
|
3
3
|
import { conversationAccess } from "../models.js";
|
|
4
4
|
/**
|
|
@@ -105,7 +105,7 @@ export function conversationInstructions(view) {
|
|
|
105
105
|
.join(" ");
|
|
106
106
|
const rules = [
|
|
107
107
|
"You hold a conversation that fills in a wizard step by step. The person also sees the wizard on the screen.",
|
|
108
|
-
`Speak ${
|
|
108
|
+
`Speak ${langNameEn(view.lang)} (${view.lang}), short and friendly, as on the phone. One sentence, then the next question.`,
|
|
109
109
|
"Ask one field at a time, in the order of the state. After each answer call set_field. When every field you can fill by voice is answered, call submit_page.",
|
|
110
110
|
"For a choice, name the options (at most four at a time) and pass exactly one of them on.",
|
|
111
111
|
"Read e-mail addresses, links and numbers back before passing them on.",
|
|
@@ -1042,6 +1042,18 @@ export function usageOf(u) {
|
|
|
1042
1042
|
cachedInputTokens: u?.cachedInputTokens ?? u?.inputTokenDetails?.cacheReadTokens ?? 0,
|
|
1043
1043
|
};
|
|
1044
1044
|
}
|
|
1045
|
+
/** A class's model as Mastra's `Agent` takes it: the AI SDK's model, typed by Mastra's own copy of it. */
|
|
1046
|
+
export function agentModel(resolved) {
|
|
1047
|
+
return resolved.model;
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* What an agent's call used over all its steps: `totalUsage` where Mastra's result has it, else
|
|
1051
|
+
* `usage`. Takes a stream's promises and a finished call's values alike.
|
|
1052
|
+
*/
|
|
1053
|
+
export async function totalUsage(out) {
|
|
1054
|
+
const result = out;
|
|
1055
|
+
return (await result.totalUsage) ?? (await result.usage);
|
|
1056
|
+
}
|
|
1045
1057
|
/** Provider cost of a text call a runtime that runs alone made; the gateway books the others itself. */
|
|
1046
1058
|
export function costOf(resolved, usage) {
|
|
1047
1059
|
// An installed client answers on its subscription: nothing to book.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { LANGS, wizardLang } from "@engenty-wizards/shared/definition";
|
|
2
|
+
import { LANGS, textLang, wizardLang } from "@engenty-wizards/shared/definition";
|
|
3
3
|
import { noteText } from "@engenty-wizards/shared/run";
|
|
4
4
|
import { and, asc, eq, inArray, isNotNull } from "drizzle-orm";
|
|
5
5
|
import { db, schema, withTenant } from "../db/client.js";
|
|
@@ -64,7 +64,7 @@ export function runsApiFor(pluginId) {
|
|
|
64
64
|
runner: input.runner ?? pluginId,
|
|
65
65
|
}).catch((err) => {
|
|
66
66
|
if (err instanceof ModelUnavailableError) {
|
|
67
|
-
throw new ServiceError("refused", errorText(err, wizardLang(version.definition)));
|
|
67
|
+
throw new ServiceError("refused", errorText(err, textLang(wizardLang(version.definition))));
|
|
68
68
|
}
|
|
69
69
|
if (err instanceof NoCreditsError) {
|
|
70
70
|
throw new ServiceError("no_credits", noteText({ code: "wizardRunOutCredits" }, wizardLang(version.definition)));
|
|
@@ -112,7 +112,7 @@ export function runsApiFor(pluginId) {
|
|
|
112
112
|
wizardId: w.id,
|
|
113
113
|
token: w.shareToken,
|
|
114
114
|
title: version.definition.title,
|
|
115
|
-
lang: wizardLang(version.definition),
|
|
115
|
+
lang: textLang(wizardLang(version.definition)),
|
|
116
116
|
},
|
|
117
117
|
]
|
|
118
118
|
: [];
|
|
@@ -200,7 +200,7 @@ export function runsApiFor(pluginId) {
|
|
|
200
200
|
call: { runId: run.id },
|
|
201
201
|
}).catch((err) => {
|
|
202
202
|
if (err instanceof ModelUnavailableError) {
|
|
203
|
-
throw new ServiceError("refused", errorText(err, wizardLang(run.definition)));
|
|
203
|
+
throw new ServiceError("refused", errorText(err, textLang(wizardLang(run.definition))));
|
|
204
204
|
}
|
|
205
205
|
throw err;
|
|
206
206
|
});
|
|
@@ -302,14 +302,16 @@ export const publicRoutes = new Hono()
|
|
|
302
302
|
throw err;
|
|
303
303
|
}
|
|
304
304
|
})
|
|
305
|
-
// The runner's own words in a language outside LANGS
|
|
305
|
+
// The runner's own words in a language outside LANGS (the wizard's own or a viewer's), booked
|
|
306
|
+
// to this wizard's owner when new.
|
|
306
307
|
.get("/wizards/:token/texts/:lang", async (c) => {
|
|
307
308
|
const found = await publishedWizard(c.req.param("token"));
|
|
308
309
|
const lang = c.req.param("lang");
|
|
309
310
|
if (!found || !isLangCode(lang) || isLang(lang)) {
|
|
310
311
|
return c.json(notFoundAnswer(c), 404);
|
|
311
312
|
}
|
|
312
|
-
|
|
313
|
+
// The wizard's own language needs the runner's words too, translation offered or not.
|
|
314
|
+
if (lang !== wizardLang(found.def) && !wantsTranslation(found.w, found.def, lang)) {
|
|
313
315
|
return refuse(c, { code: "translationOff" }, 403);
|
|
314
316
|
}
|
|
315
317
|
try {
|
|
@@ -6,8 +6,6 @@ const WRITE_CHUNK = 48 * 1024;
|
|
|
6
6
|
/** What the engenty-sandbox image carries. The container sits on Docker's default bridge. */
|
|
7
7
|
export const DOCKER_CAPABILITIES = {
|
|
8
8
|
commands: ["bash", "python3", "pip", "uv", "node", "npm", "bun", "jq", "curl", "git", "unzip"],
|
|
9
|
-
python: "shell",
|
|
10
|
-
pythonPackages: "install what you need with pip or uv",
|
|
11
9
|
network: "open internet",
|
|
12
10
|
uses: "calculations, charts or file conversion",
|
|
13
11
|
workdir: WORKDIR,
|
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
import { env } from "../env.js";
|
|
2
|
-
import { AGENTOS_CAPABILITIES, AgentOsSandbox } from "./agentos.js";
|
|
3
2
|
import { DOCKER_CAPABILITIES, DockerRunSandbox } from "./docker.js";
|
|
4
3
|
/** What this server's sandbox runs, or null when it is switched off. */
|
|
5
4
|
export function sandboxCapabilities() {
|
|
6
5
|
if (env.sandbox === "off") {
|
|
7
6
|
return null;
|
|
8
7
|
}
|
|
9
|
-
return
|
|
8
|
+
return DOCKER_CAPABILITIES;
|
|
10
9
|
}
|
|
11
10
|
export function createSandbox(runId) {
|
|
12
11
|
if (env.sandbox === "off") {
|
|
13
12
|
throw new Error("The sandbox is switched off on this server.");
|
|
14
13
|
}
|
|
15
|
-
return env.
|
|
16
|
-
? new AgentOsSandbox()
|
|
17
|
-
: new DockerRunSandbox(runId, env.sandboxImage);
|
|
14
|
+
return new DockerRunSandbox(runId, env.sandboxImage);
|
|
18
15
|
}
|
|
19
16
|
export function shellToolDescription(caps) {
|
|
20
|
-
|
|
21
|
-
return `Run a shell command in a private Linux sandbox (${caps.commands.join(", ")}). Working directory ${caps.workdir}. Network: ${caps.network}.${python}`;
|
|
22
|
-
}
|
|
23
|
-
export function pythonToolDescription(caps) {
|
|
24
|
-
return `Run Python code in the sandbox (${caps.pythonPackages}). Working directory ${caps.workdir}, shared with run_command and export_file.`;
|
|
17
|
+
return `Run a shell command in a private Linux sandbox (${caps.commands.join(", ")}). Working directory ${caps.workdir}. Network: ${caps.network}.`;
|
|
25
18
|
}
|
|
26
19
|
/** The authoring guide's line about the sandbox tool. */
|
|
27
20
|
export function sandboxGuideLine() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { wizardLang, } from "@engenty-wizards/shared/definition";
|
|
1
|
+
import { textLang, wizardLang, } from "@engenty-wizards/shared/definition";
|
|
2
2
|
import { standaloneEntry } from "@engenty-wizards/shared/fit";
|
|
3
3
|
import { pluginsOf } from "@engenty-wizards/shared/marketplace";
|
|
4
4
|
import { noteText } from "@engenty-wizards/shared/run";
|
|
@@ -141,7 +141,7 @@ function answerText(field, value, lang) {
|
|
|
141
141
|
/** What the person answered on the run's pages, by the fields' labels, in the order asked. */
|
|
142
142
|
function answersOf(run) {
|
|
143
143
|
const answers = [];
|
|
144
|
-
const lang = wizardLang(run.definition);
|
|
144
|
+
const lang = textLang(wizardLang(run.definition));
|
|
145
145
|
for (const step of run.definition.steps) {
|
|
146
146
|
if (step.type !== "page") {
|
|
147
147
|
continue;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { wizardLang } from "@engenty-wizards/shared/definition";
|
|
1
|
+
import { textLang, wizardLang } from "@engenty-wizards/shared/definition";
|
|
2
2
|
import { note } from "@engenty-wizards/shared/run";
|
|
3
3
|
import { BUILT_IN_RUNNERS, CONVERSATION_RUNNERS, DEFAULT_RUNNER, DEFAULT_RUNNER_SETTINGS, runnerFit, } from "@engenty-wizards/shared/runners";
|
|
4
4
|
import { env } from "../env.js";
|
|
@@ -164,7 +164,7 @@ export async function servePageRunner(w, def, runnerId, rest, request) {
|
|
|
164
164
|
export async function publicRunners(w, def) {
|
|
165
165
|
const available = await runnersOf();
|
|
166
166
|
const settings = runnerSettingsOf(w, available);
|
|
167
|
-
const lang = wizardLang(def);
|
|
167
|
+
const lang = textLang(wizardLang(def));
|
|
168
168
|
return settings.enabled
|
|
169
169
|
.map((id) => available.find((r) => r.id === id))
|
|
170
170
|
.filter((r) => Boolean(r) && !r?.problem)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CATEGORY_TYPES, dayOf, } from "@engenty-wizards/shared/knowledge";
|
|
2
|
+
import { readNumber } from "@engenty-wizards/shared/numbers";
|
|
2
3
|
import { note } from "@engenty-wizards/shared/run";
|
|
3
4
|
import { and, asc, eq, inArray, isNotNull, or, sql } from "drizzle-orm";
|
|
4
5
|
import { nanoid } from "nanoid";
|
|
@@ -42,32 +43,9 @@ function refWhere(ref) {
|
|
|
42
43
|
return eq(t.fileId, ref.fileId);
|
|
43
44
|
}
|
|
44
45
|
// --- typed values ---------------------------------------------------------------
|
|
45
|
-
/** A number as
|
|
46
|
+
/** A number with what stands around it, as `readNumber` reads it: `38 h`, `19 %`, `1,5`. */
|
|
46
47
|
export function numberOf(value) {
|
|
47
|
-
|
|
48
|
-
return Number.isFinite(value) ? value : null;
|
|
49
|
-
}
|
|
50
|
-
if (typeof value !== "string") {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
let s = value.replace(/[^\d,.-]/g, "");
|
|
54
|
-
if (!/\d/.test(s)) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
if (s.includes(",") && s.includes(".")) {
|
|
58
|
-
s =
|
|
59
|
-
s.lastIndexOf(",") > s.lastIndexOf(".")
|
|
60
|
-
? s.replace(/\./g, "").replace(",", ".")
|
|
61
|
-
: s.replace(/,/g, "");
|
|
62
|
-
}
|
|
63
|
-
else if (s.includes(",")) {
|
|
64
|
-
s = /^-?\d{1,3}(,\d{3}){2,}$/.test(s) ? s.replace(/,/g, "") : s.replace(",", ".");
|
|
65
|
-
}
|
|
66
|
-
else if (/^-?\d{1,3}(\.\d{3})+$/.test(s)) {
|
|
67
|
-
s = s.replace(/\./g, "");
|
|
68
|
-
}
|
|
69
|
-
const n = Number(s);
|
|
70
|
-
return Number.isFinite(n) ? n : null;
|
|
48
|
+
return readNumber(value, { units: true });
|
|
71
49
|
}
|
|
72
50
|
/** A day, or a day and a time, as milliseconds (UTC, as written: no time zone is applied). */
|
|
73
51
|
export function dateOf(value) {
|
|
@@ -48,11 +48,14 @@ async function translate(texts, opts) {
|
|
|
48
48
|
chunks.push(keys.slice(i, i + size));
|
|
49
49
|
}
|
|
50
50
|
const model = await textModel("standard", { effort: "low" });
|
|
51
|
+
// The model sees short keys (t0, t1, …): some providers allow only letters, digits, "_", "."
|
|
52
|
+
// and "-" in a schema's property names, and a text's own key may hold anything.
|
|
51
53
|
const call = async (chunk) => {
|
|
54
|
+
const ids = chunk.map((_, i) => `t${i}`);
|
|
52
55
|
const schema = jsonSchema({
|
|
53
56
|
type: "object",
|
|
54
|
-
properties: Object.fromEntries(
|
|
55
|
-
required:
|
|
57
|
+
properties: Object.fromEntries(ids.map((id) => [id, { type: "string" }])),
|
|
58
|
+
required: ids,
|
|
56
59
|
additionalProperties: false,
|
|
57
60
|
});
|
|
58
61
|
const result = await generateText({
|
|
@@ -67,9 +70,9 @@ async function translate(texts, opts) {
|
|
|
67
70
|
]
|
|
68
71
|
.filter(Boolean)
|
|
69
72
|
.join("\n"),
|
|
70
|
-
prompt: JSON.stringify(Object.fromEntries(chunk.map((k) => [
|
|
73
|
+
prompt: JSON.stringify(Object.fromEntries(chunk.map((k, i) => [ids[i], texts[k]])), null, 1),
|
|
71
74
|
});
|
|
72
|
-
return result.output;
|
|
75
|
+
return Object.fromEntries(chunk.map((k, i) => [k, result.output[ids[i]]]));
|
|
73
76
|
};
|
|
74
77
|
const out = {};
|
|
75
78
|
const answers = await Promise.all(chunks.map(call));
|
|
@@ -9,7 +9,7 @@ import { nodeScript, PAGE_SCRIPT, settleScript, targetScript, } from "../browser
|
|
|
9
9
|
import { env } from "../env.js";
|
|
10
10
|
import { costOf, textModel } from "../models.js";
|
|
11
11
|
import { assertPublicUrl } from "./net-guard.js";
|
|
12
|
-
import { attempt } from "./shared.js";
|
|
12
|
+
import { attempt, imageOutput } from "./shared.js";
|
|
13
13
|
/** The page as an agent step reads it; retried while the page is between two documents. */
|
|
14
14
|
export async function readPage(page) {
|
|
15
15
|
for (let attempt = 0; attempt < 5; attempt++) {
|
|
@@ -473,19 +473,7 @@ export async function browserTools(ctx, assets, files) {
|
|
|
473
473
|
}
|
|
474
474
|
return { url: p.url(), saved, screenshot: Buffer.from(png).toString("base64") };
|
|
475
475
|
}),
|
|
476
|
-
toModelOutput: (
|
|
477
|
-
if (!out?.screenshot) {
|
|
478
|
-
return { type: "json", value: out };
|
|
479
|
-
}
|
|
480
|
-
const { screenshot, ...rest } = out;
|
|
481
|
-
return {
|
|
482
|
-
type: "content",
|
|
483
|
-
value: [
|
|
484
|
-
{ type: "text", text: JSON.stringify(rest) },
|
|
485
|
-
{ type: "image-data", data: screenshot, mediaType: "image/png" },
|
|
486
|
-
],
|
|
487
|
-
};
|
|
488
|
-
},
|
|
476
|
+
toModelOutput: imageOutput("screenshot"),
|
|
489
477
|
});
|
|
490
478
|
const handOver = async (input) => {
|
|
491
479
|
const p = await page();
|