@engenty/wizards-runtime 0.0.0-stage → 0.7.5
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/LICENSE +110 -0
- package/README.md +214 -2
- package/apps/runtime/dist/agents/architect.js +444 -0
- package/apps/runtime/dist/agents/studio-assistant.js +883 -0
- package/apps/runtime/dist/agents/subscription.js +207 -0
- package/apps/runtime/dist/agents/thought.js +21 -0
- package/apps/runtime/dist/agents/website.js +480 -0
- package/apps/runtime/dist/app.js +359 -0
- package/apps/runtime/dist/apple/wizards-apple +0 -0
- package/apps/runtime/dist/assistant/browser.js +26 -0
- package/apps/runtime/dist/assistant/docs.js +158 -0
- package/apps/runtime/dist/assistant/knows.js +353 -0
- package/apps/runtime/dist/assistant/primer.js +26 -0
- package/apps/runtime/dist/assistant/runs.js +24 -0
- package/apps/runtime/dist/auth/account.js +175 -0
- package/apps/runtime/dist/auth/device.js +112 -0
- package/apps/runtime/dist/auth/index.js +358 -0
- package/apps/runtime/dist/auth/keys.js +55 -0
- package/apps/runtime/dist/auth/local-ticket.js +26 -0
- package/apps/runtime/dist/authoring/example.js +74 -0
- package/apps/runtime/dist/authoring/guide.js +433 -0
- package/apps/runtime/dist/authoring/ops.js +117 -0
- package/apps/runtime/dist/browser/fast/action-space.js +160 -0
- package/apps/runtime/dist/browser/fast/classifier.js +91 -0
- package/apps/runtime/dist/browser/fast/decide.js +171 -0
- package/apps/runtime/dist/browser/fast/driver.js +141 -0
- package/apps/runtime/dist/browser/fast/instructions.js +33 -0
- package/apps/runtime/dist/browser/fast/run.js +249 -0
- package/apps/runtime/dist/browser/fast/span-picker.js +153 -0
- package/apps/runtime/dist/browser/fast/text-helper.js +178 -0
- package/apps/runtime/dist/browser/snapshot.js +335 -0
- package/apps/runtime/dist/cli/account.js +385 -0
- package/apps/runtime/dist/cli/autostart.js +182 -0
- package/apps/runtime/dist/cli/autoupdate.js +221 -0
- package/apps/runtime/dist/cli/clients.js +44 -0
- package/apps/runtime/dist/cli/connect.js +540 -0
- package/apps/runtime/dist/cli/environment.js +93 -0
- package/apps/runtime/dist/cli/exec.js +45 -0
- package/apps/runtime/dist/cli/features.js +50 -0
- package/apps/runtime/dist/cli/home.js +88 -0
- package/apps/runtime/dist/cli/install-env.js +33 -0
- package/apps/runtime/dist/cli/machine.js +107 -0
- package/apps/runtime/dist/cli/main.js +222 -0
- package/apps/runtime/dist/cli/mcp.js +136 -0
- package/apps/runtime/dist/cli/menu.js +202 -0
- package/apps/runtime/dist/cli/processes.js +72 -0
- package/apps/runtime/dist/cli/report.js +66 -0
- package/apps/runtime/dist/cli/setup.js +335 -0
- package/apps/runtime/dist/cli/start.js +276 -0
- package/apps/runtime/dist/cli/status.js +203 -0
- package/apps/runtime/dist/cli/ui.js +59 -0
- 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 +86 -0
- package/apps/runtime/dist/cli/validate.js +75 -0
- package/apps/runtime/dist/cli/versions.js +123 -0
- package/apps/runtime/dist/connectors/builtin.js +86 -0
- package/apps/runtime/dist/connectors/external.js +409 -0
- package/apps/runtime/dist/connectors/gmail.js +107 -0
- package/apps/runtime/dist/connectors/imap.js +261 -0
- package/apps/runtime/dist/connectors/index.js +325 -0
- package/apps/runtime/dist/connectors/mail.js +72 -0
- package/apps/runtime/dist/connectors/outlook.js +90 -0
- package/apps/runtime/dist/connectors/plugin-connections.js +343 -0
- package/apps/runtime/dist/connectors/via-account.js +65 -0
- package/apps/runtime/dist/credits/credits.js +100 -0
- package/apps/runtime/dist/credits/estimate.js +152 -0
- package/apps/runtime/dist/db/adopt.js +149 -0
- package/apps/runtime/dist/db/client.js +173 -0
- package/apps/runtime/dist/db/control-schema.js +133 -0
- package/apps/runtime/dist/db/migrations/control/0000_last_absorbing_man.sql +47 -0
- package/apps/runtime/dist/db/migrations/control/0001_gorgeous_nicolaos.sql +41 -0
- package/apps/runtime/dist/db/migrations/control/0002_secret_penance.sql +1 -0
- package/apps/runtime/dist/db/migrations/control/0003_nasty_mordo.sql +3 -0
- package/apps/runtime/dist/db/migrations/control/0004_breezy_eddie_brock.sql +6 -0
- package/apps/runtime/dist/db/migrations/control/0005_chilly_oracle.sql +1 -0
- package/apps/runtime/dist/db/migrations/control/0006_sleepy_mojo.sql +1 -0
- package/apps/runtime/dist/db/migrations/control/0007_common_the_hand.sql +15 -0
- package/apps/runtime/dist/db/migrations/control/0008_runner.sql +8 -0
- package/apps/runtime/dist/db/migrations/control/0009_plugin_claim.sql +9 -0
- package/apps/runtime/dist/db/migrations/control/0010_tan_snowbird.sql +6 -0
- package/apps/runtime/dist/db/migrations/control/meta/0000_snapshot.json +311 -0
- package/apps/runtime/dist/db/migrations/control/meta/0001_snapshot.json +599 -0
- package/apps/runtime/dist/db/migrations/control/meta/0002_snapshot.json +607 -0
- package/apps/runtime/dist/db/migrations/control/meta/0003_snapshot.json +311 -0
- package/apps/runtime/dist/db/migrations/control/meta/0004_snapshot.json +350 -0
- package/apps/runtime/dist/db/migrations/control/meta/0005_snapshot.json +358 -0
- package/apps/runtime/dist/db/migrations/control/meta/0006_snapshot.json +365 -0
- package/apps/runtime/dist/db/migrations/control/meta/0007_snapshot.json +460 -0
- package/apps/runtime/dist/db/migrations/control/meta/0008_snapshot.json +505 -0
- package/apps/runtime/dist/db/migrations/control/meta/0009_snapshot.json +561 -0
- package/apps/runtime/dist/db/migrations/control/meta/0010_snapshot.json +600 -0
- package/apps/runtime/dist/db/migrations/control/meta/_journal.json +83 -0
- package/apps/runtime/dist/db/migrations/tenant/0000_eminent_freak.sql +188 -0
- package/apps/runtime/dist/db/migrations/tenant/0001_salty_master_chief.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0002_cloudy_firebird.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0003_brown_warbound.sql +52 -0
- package/apps/runtime/dist/db/migrations/tenant/0004_romantic_nocturne.sql +6 -0
- package/apps/runtime/dist/db/migrations/tenant/0005_reflective_forgotten_one.sql +2 -0
- package/apps/runtime/dist/db/migrations/tenant/0006_hard_war_machine.sql +30 -0
- package/apps/runtime/dist/db/migrations/tenant/0007_space_data.sql +43 -0
- package/apps/runtime/dist/db/migrations/tenant/0008_run_event_note.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0009_knowledge.sql +104 -0
- package/apps/runtime/dist/db/migrations/tenant/0010_plugin_connection.sql +15 -0
- package/apps/runtime/dist/db/migrations/tenant/0011_runners.sql +2 -0
- package/apps/runtime/dist/db/migrations/tenant/0012_assistant_chat.sql +40 -0
- package/apps/runtime/dist/db/migrations/tenant/0013_run_origin.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0014_run_answer.sql +2 -0
- package/apps/runtime/dist/db/migrations/tenant/0015_device_calls.sql +13 -0
- package/apps/runtime/dist/db/migrations/tenant/0016_even_trish_tilby.sql +12 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0000_snapshot.json +1323 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0001_snapshot.json +1330 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0002_snapshot.json +1337 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0003_snapshot.json +1605 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0004_snapshot.json +1645 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0005_snapshot.json +1659 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0006_snapshot.json +1696 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0007_snapshot.json +2012 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0008_snapshot.json +2019 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0009_snapshot.json +2837 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0010_snapshot.json +2942 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0011_snapshot.json +2956 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0012_snapshot.json +3132 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0013_snapshot.json +3139 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0014_snapshot.json +3154 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0015_snapshot.json +3243 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0016_snapshot.json +3326 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +125 -0
- package/apps/runtime/dist/db/schema.js +602 -0
- package/apps/runtime/dist/debug.js +196 -0
- package/apps/runtime/dist/docs/dev/index.md +41 -0
- package/apps/runtime/dist/docs/dev/meta.json +7 -0
- package/apps/runtime/dist/docs/dev/plugins/connections.md +74 -0
- package/apps/runtime/dist/docs/dev/plugins/events.md +89 -0
- package/apps/runtime/dist/docs/dev/plugins/examples.md +101 -0
- package/apps/runtime/dist/docs/dev/plugins/index.md +78 -0
- package/apps/runtime/dist/docs/dev/plugins/layout.md +90 -0
- package/apps/runtime/dist/docs/dev/plugins/meta.json +28 -0
- package/apps/runtime/dist/docs/dev/plugins/quick-start.md +94 -0
- package/apps/runtime/dist/docs/dev/plugins/reference.md +348 -0
- package/apps/runtime/dist/docs/dev/plugins/reloading.md +76 -0
- package/apps/runtime/dist/docs/dev/plugins/routes.md +185 -0
- package/apps/runtime/dist/docs/dev/plugins/runners.md +230 -0
- package/apps/runtime/dist/docs/dev/plugins/server.md +281 -0
- package/apps/runtime/dist/docs/dev/plugins/shipping.md +79 -0
- package/apps/runtime/dist/docs/dev/plugins/space.md +297 -0
- package/apps/runtime/dist/docs/dev/plugins/starters.md +70 -0
- package/apps/runtime/dist/docs/dev/plugins/studio.md +363 -0
- package/apps/runtime/dist/docs/dev/plugins/styling.md +129 -0
- package/apps/runtime/dist/docs/dev/plugins/tables.md +104 -0
- package/apps/runtime/dist/docs/dev/plugins/tools.md +139 -0
- package/apps/runtime/dist/docs/dev/wizard-format.md +125 -0
- package/apps/runtime/dist/docs/meta.json +3 -0
- package/apps/runtime/dist/docs/user/ai-apps.md +112 -0
- package/apps/runtime/dist/docs/user/assistant.md +77 -0
- package/apps/runtime/dist/docs/user/build-a-wizard.md +116 -0
- package/apps/runtime/dist/docs/user/command-line.md +83 -0
- package/apps/runtime/dist/docs/user/connectors.md +90 -0
- package/apps/runtime/dist/docs/user/create-account.md +52 -0
- package/apps/runtime/dist/docs/user/data.md +66 -0
- package/apps/runtime/dist/docs/user/first-start.md +60 -0
- package/apps/runtime/dist/docs/user/index.md +49 -0
- package/apps/runtime/dist/docs/user/install.md +123 -0
- package/apps/runtime/dist/docs/user/local-only.md +42 -0
- package/apps/runtime/dist/docs/user/meta.json +32 -0
- package/apps/runtime/dist/docs/user/models.md +156 -0
- package/apps/runtime/dist/docs/user/plugins.md +71 -0
- package/apps/runtime/dist/docs/user/run.md +98 -0
- package/apps/runtime/dist/docs/user/server.md +52 -0
- package/apps/runtime/dist/docs/user/space.md +126 -0
- package/apps/runtime/dist/docs/user/steps.md +220 -0
- package/apps/runtime/dist/docs/user/test-and-share.md +224 -0
- package/apps/runtime/dist/docs/user/troubleshooting.md +67 -0
- package/apps/runtime/dist/docs/user/wordpress.md +121 -0
- package/apps/runtime/dist/documents/parse.js +246 -0
- package/apps/runtime/dist/engenty/connections-external/build-connector.js +193 -0
- package/apps/runtime/dist/engenty/connections-external/errors.js +11 -0
- package/apps/runtime/dist/engenty/connections-external/import-service.js +270 -0
- package/apps/runtime/dist/engenty/connections-external/importer/classify.js +34 -0
- package/apps/runtime/dist/engenty/connections-external/importer/map-auth.js +185 -0
- package/apps/runtime/dist/engenty/connections-external/importer/normalize-mcp.js +63 -0
- package/apps/runtime/dist/engenty/connections-external/importer/normalize-openapi.js +130 -0
- package/apps/runtime/dist/engenty/connections-external/invoke/http-invoker.js +143 -0
- package/apps/runtime/dist/engenty/connections-external/invoke/mcp-client.js +143 -0
- package/apps/runtime/dist/engenty/connections-external/net/guarded-fetch.js +119 -0
- package/apps/runtime/dist/engenty/connections-external/oauth-dcr.js +88 -0
- package/apps/runtime/dist/engenty/connections-external/registry-client.js +336 -0
- package/apps/runtime/dist/engenty/connections-external/registry-source.js +82 -0
- package/apps/runtime/dist/engenty/connections-external/types.js +7 -0
- package/apps/runtime/dist/engenty/connections-github/connector.js +209 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/calendar.js +381 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/contacts.js +94 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/drive.js +290 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/gmail.js +567 -0
- package/apps/runtime/dist/engenty/connections-google/definitions.js +11 -0
- package/apps/runtime/dist/engenty/connections-google/shared.js +73 -0
- package/apps/runtime/dist/engenty/connections-hubspot/connector.js +140 -0
- package/apps/runtime/dist/engenty/connections-microsoft/action.js +17 -0
- package/apps/runtime/dist/engenty/connections-microsoft/graph.js +114 -0
- package/apps/runtime/dist/engenty/connections-microsoft/onedrive.js +286 -0
- package/apps/runtime/dist/engenty/connections-microsoft/outlook.js +492 -0
- package/apps/runtime/dist/engenty/connections-s3/s3.js +328 -0
- package/apps/runtime/dist/engenty/connections-sdk/files-capability.js +86 -0
- package/apps/runtime/dist/engenty/connections-sdk/oauth2.js +150 -0
- package/apps/runtime/dist/engenty/connections-sdk/registry.js +89 -0
- package/apps/runtime/dist/engenty/connections-sdk/storage-capability.js +84 -0
- package/apps/runtime/dist/engenty/connections-sdk/types.js +30 -0
- package/apps/runtime/dist/engenty/connections-slack/connector.js +360 -0
- package/apps/runtime/dist/engenty/csv-import/csv-matrix.js +178 -0
- package/apps/runtime/dist/engenty/csv-import/import-sources.js +87 -0
- package/apps/runtime/dist/engenty/csv-import/parse-csv.js +129 -0
- package/apps/runtime/dist/engenty/csv-import/serialize-csv.js +20 -0
- package/apps/runtime/dist/engenty/csv-import/types.js +1 -0
- package/apps/runtime/dist/engenty/csv-import/xlsx-workbook.js +159 -0
- package/apps/runtime/dist/engenty/doc-converter/interface.js +9 -0
- package/apps/runtime/dist/engenty/doc-converter/page-break.js +92 -0
- package/apps/runtime/dist/engenty/doc-converter/providers/local/index.js +72 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/classifier.js +30 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/index.js +4 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/invoice.js +73 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/receipt.js +63 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/shared.js +40 -0
- package/apps/runtime/dist/engenty/search-index/chunks.js +164 -0
- package/apps/runtime/dist/engenty/search-index/contracts.js +9 -0
- package/apps/runtime/dist/engenty/shims/connections-sdk.js +39 -0
- package/apps/runtime/dist/engenty/shims/plugin-sdk.js +1 -0
- package/apps/runtime/dist/engenty/shims/web-ingest.js +19 -0
- package/apps/runtime/dist/engenty/web-ingest/ssrf.js +277 -0
- package/apps/runtime/dist/engine/asks.js +82 -0
- package/apps/runtime/dist/engine/decide.js +82 -0
- package/apps/runtime/dist/engine/device.js +194 -0
- package/apps/runtime/dist/engine/events.js +128 -0
- package/apps/runtime/dist/engine/input.js +208 -0
- package/apps/runtime/dist/engine/owner.js +32 -0
- package/apps/runtime/dist/engine/prepare.js +90 -0
- package/apps/runtime/dist/engine/push.js +58 -0
- package/apps/runtime/dist/engine/requirements.js +355 -0
- package/apps/runtime/dist/engine/resources.js +198 -0
- package/apps/runtime/dist/engine/runner.js +970 -0
- package/apps/runtime/dist/engine/steps.js +1000 -0
- package/apps/runtime/dist/engine/surface.js +91 -0
- package/apps/runtime/dist/engine/template.js +240 -0
- package/apps/runtime/dist/engine/types.js +4 -0
- package/apps/runtime/dist/env.js +295 -0
- package/apps/runtime/dist/features.js +123 -0
- package/apps/runtime/dist/files/blobs.js +22 -0
- package/apps/runtime/dist/files/objects.js +86 -0
- package/apps/runtime/dist/files/storage.js +120 -0
- package/apps/runtime/dist/files/web-images.js +101 -0
- package/apps/runtime/dist/film/project.js +257 -0
- package/apps/runtime/dist/film/skills.js +91 -0
- package/apps/runtime/dist/film/sounds.js +122 -0
- package/apps/runtime/dist/film/step.js +289 -0
- package/apps/runtime/dist/film/tools.js +99 -0
- package/apps/runtime/dist/geocode.js +58 -0
- package/apps/runtime/dist/harness/antigravity.js +129 -0
- package/apps/runtime/dist/harness/apple.js +270 -0
- package/apps/runtime/dist/harness/claude.js +147 -0
- package/apps/runtime/dist/harness/codex.js +229 -0
- package/apps/runtime/dist/harness/cursor.js +93 -0
- package/apps/runtime/dist/harness/env.js +124 -0
- package/apps/runtime/dist/harness/index.js +95 -0
- package/apps/runtime/dist/harness/model.js +279 -0
- package/apps/runtime/dist/harness/prompt.js +121 -0
- package/apps/runtime/dist/harness/terminal.js +194 -0
- package/apps/runtime/dist/harness/types.js +7 -0
- package/apps/runtime/dist/index.js +113 -0
- package/apps/runtime/dist/limits.js +81 -0
- package/apps/runtime/dist/link-preview.js +60 -0
- package/apps/runtime/dist/log.js +106 -0
- package/apps/runtime/dist/manage.js +249 -0
- package/apps/runtime/dist/mcp/auth.js +161 -0
- package/apps/runtime/dist/mcp/bridge.js +59 -0
- package/apps/runtime/dist/mcp/flow-app.js +97 -0
- package/apps/runtime/dist/mcp/handler.js +54 -0
- package/apps/runtime/dist/mcp/scopes.js +14 -0
- package/apps/runtime/dist/mcp/seen.js +65 -0
- package/apps/runtime/dist/mcp/tools.js +572 -0
- package/apps/runtime/dist/media/conversation.js +167 -0
- package/apps/runtime/dist/media/ffmpeg.js +121 -0
- package/apps/runtime/dist/media/generate.js +120 -0
- package/apps/runtime/dist/media/marking.js +121 -0
- package/apps/runtime/dist/media/transcribe.js +198 -0
- package/apps/runtime/dist/model-errors.js +42 -0
- package/apps/runtime/dist/models.js +1164 -0
- package/apps/runtime/dist/plugin.js +61 -0
- package/apps/runtime/dist/plugins/channels.js +78 -0
- package/apps/runtime/dist/plugins/claims.js +56 -0
- package/apps/runtime/dist/plugins/discovery.js +155 -0
- package/apps/runtime/dist/plugins/events.js +63 -0
- package/apps/runtime/dist/plugins/jobs.js +64 -0
- package/apps/runtime/dist/plugins/loader.js +508 -0
- package/apps/runtime/dist/plugins/migrations.js +69 -0
- package/apps/runtime/dist/plugins/pro-keys.js +34 -0
- package/apps/runtime/dist/plugins/pro-state.js +83 -0
- package/apps/runtime/dist/plugins/pro.js +310 -0
- package/apps/runtime/dist/plugins/public.js +44 -0
- package/apps/runtime/dist/plugins/registry.js +122 -0
- package/apps/runtime/dist/plugins/runs.js +210 -0
- package/apps/runtime/dist/render/browser-path.js +54 -0
- package/apps/runtime/dist/render/chromium.js +98 -0
- package/apps/runtime/dist/render/convert.js +158 -0
- package/apps/runtime/dist/render/downloads.js +299 -0
- package/apps/runtime/dist/render/guard.js +25 -0
- package/apps/runtime/dist/render/shaped.js +137 -0
- package/apps/runtime/dist/routes/api.js +440 -0
- package/apps/runtime/dist/routes/debug.js +42 -0
- package/apps/runtime/dist/routes/delivery.js +90 -0
- package/apps/runtime/dist/routes/internal.js +81 -0
- package/apps/runtime/dist/routes/lang.js +25 -0
- package/apps/runtime/dist/routes/marketplace.js +43 -0
- package/apps/runtime/dist/routes/plugin-public.js +113 -0
- package/apps/runtime/dist/routes/plugins.js +212 -0
- package/apps/runtime/dist/routes/runs.js +1422 -0
- package/apps/runtime/dist/routes/studio.js +1629 -0
- package/apps/runtime/dist/routes/wizard-stream.js +30 -0
- package/apps/runtime/dist/running.js +39 -0
- package/apps/runtime/dist/sandbox/docker.js +75 -0
- package/apps/runtime/dist/sandbox/index.js +23 -0
- package/apps/runtime/dist/sandbox/types.js +1 -0
- package/apps/runtime/dist/secrets/crypto.js +28 -0
- package/apps/runtime/dist/secrets/signing.js +52 -0
- package/apps/runtime/dist/secrets/vault.js +59 -0
- package/apps/runtime/dist/services/access.js +88 -0
- package/apps/runtime/dist/services/api.js +248 -0
- package/apps/runtime/dist/services/architect.js +95 -0
- package/apps/runtime/dist/services/assistant-chats.js +311 -0
- package/apps/runtime/dist/services/brand.js +85 -0
- package/apps/runtime/dist/services/cloud.js +456 -0
- package/apps/runtime/dist/services/draft-events.js +27 -0
- package/apps/runtime/dist/services/engenty-shapes.js +71 -0
- package/apps/runtime/dist/services/errors.js +29 -0
- package/apps/runtime/dist/services/files.js +178 -0
- package/apps/runtime/dist/services/knowledge-model.js +559 -0
- package/apps/runtime/dist/services/knowledge.js +721 -0
- package/apps/runtime/dist/services/links.js +16 -0
- package/apps/runtime/dist/services/marketplace.js +492 -0
- package/apps/runtime/dist/services/onboarding.js +152 -0
- package/apps/runtime/dist/services/package-file.js +82 -0
- package/apps/runtime/dist/services/package.js +170 -0
- package/apps/runtime/dist/services/profile.js +51 -0
- package/apps/runtime/dist/services/project-files.js +439 -0
- package/apps/runtime/dist/services/project-index.js +707 -0
- package/apps/runtime/dist/services/projects.js +387 -0
- package/apps/runtime/dist/services/relevance.js +58 -0
- package/apps/runtime/dist/services/runners.js +178 -0
- package/apps/runtime/dist/services/runs.js +524 -0
- package/apps/runtime/dist/services/shares.js +130 -0
- package/apps/runtime/dist/services/site.js +270 -0
- package/apps/runtime/dist/services/space-categories.js +861 -0
- package/apps/runtime/dist/services/space-data.js +792 -0
- package/apps/runtime/dist/services/space-origin.js +207 -0
- package/apps/runtime/dist/services/spaces.js +656 -0
- package/apps/runtime/dist/services/translations.js +306 -0
- package/apps/runtime/dist/services/widgets.js +34 -0
- package/apps/runtime/dist/services/wizard-icon.js +157 -0
- package/apps/runtime/dist/services/wizards.js +420 -0
- package/apps/runtime/dist/settings.js +16 -0
- package/apps/runtime/dist/store/errors.js +4 -0
- package/apps/runtime/dist/store/index.js +299 -0
- package/apps/runtime/dist/store/shared-lists.js +123 -0
- package/apps/runtime/dist/tenants/control.js +238 -0
- package/apps/runtime/dist/tenants/tenant.js +26 -0
- package/apps/runtime/dist/texts.js +207 -0
- package/apps/runtime/dist/tools/browser.js +703 -0
- package/apps/runtime/dist/tools/connector.js +204 -0
- package/apps/runtime/dist/tools/index.js +291 -0
- package/apps/runtime/dist/tools/mail.js +104 -0
- package/apps/runtime/dist/tools/net-guard.js +170 -0
- package/apps/runtime/dist/tools/pages.js +81 -0
- package/apps/runtime/dist/tools/plugin.js +180 -0
- package/apps/runtime/dist/tools/project.js +154 -0
- package/apps/runtime/dist/tools/shared.js +69 -0
- package/apps/runtime/dist/tools/store.js +238 -0
- package/apps/runtime/dist/update.js +113 -0
- package/apps/runtime/dist/widgets/bundle.js +99 -0
- package/apps/runtime/dist/widgets/render.js +329 -0
- package/apps/runtime/dist/widgets/runtime.js +108 -0
- package/apps/runtime/dist/widgets/script.js +30 -0
- package/apps/runtime/dist/widgets/step.js +223 -0
- package/apps/web/dist/ai-labels/ai-black.svg +20 -0
- package/apps/web/dist/ai-labels/ai-generated-black.svg +31 -0
- package/apps/web/dist/ai-labels/ai-generated-white.svg +32 -0
- package/apps/web/dist/ai-labels/ai-modified-black.svg +30 -0
- package/apps/web/dist/ai-labels/ai-modified-white.svg +31 -0
- package/apps/web/dist/ai-labels/ai-white.svg +21 -0
- package/apps/web/dist/assets/AboutDialog-D4mvc-zu.js +1 -0
- package/apps/web/dist/assets/ChangelogDialog-DLqpJsF6.js +1 -0
- package/apps/web/dist/assets/ChatView-3laypRcE.js +10 -0
- package/apps/web/dist/assets/EditorPage-BalA5U2Y.js +3 -0
- package/apps/web/dist/assets/FlowPreview-CusP7ci9.js +1 -0
- package/apps/web/dist/assets/Harness-BrP-ENHg.css +1 -0
- package/apps/web/dist/assets/Harness-iYLtOLaK.js +16 -0
- package/apps/web/dist/assets/NewWizardChannels-CbwJ9A56.js +1 -0
- package/apps/web/dist/assets/NewWizardPage-BOBYiB9i.js +7 -0
- package/apps/web/dist/assets/OnboardingPage-CUwCiVIr.js +1 -0
- package/apps/web/dist/assets/ProjectPage-Bb16MdYG.js +1 -0
- package/apps/web/dist/assets/PublicRunner-BCt7Q6C-.js +1 -0
- package/apps/web/dist/assets/ReadOnly-kIHX2TEy.js +1 -0
- package/apps/web/dist/assets/RunDrawer-hZ9oeSXu.js +4 -0
- package/apps/web/dist/assets/RunnerView-DUS9fPmx.js +1 -0
- package/apps/web/dist/assets/SettingsPage-BzwR50fi.js +1 -0
- package/apps/web/dist/assets/SetupPage-gAp7rPxp.js +1 -0
- package/apps/web/dist/assets/SharePage-DqZWHva_.js +1 -0
- package/apps/web/dist/assets/changelog-DugmCNSf.js +1 -0
- package/apps/web/dist/assets/engenty-eXZ-EAwd.js +1 -0
- package/apps/web/dist/assets/esm-DXeMu8Lb.js +7 -0
- package/apps/web/dist/assets/fluffy-engenty-ClRou7yu.js +456 -0
- package/apps/web/dist/assets/index-AfUa8xKD.css +1 -0
- package/apps/web/dist/assets/index-C5Sm7tTR.js +14 -0
- package/apps/web/dist/assets/jsx-runtime-BNakU3Ej.js +1 -0
- package/apps/web/dist/assets/lock-R_CfU0Xo.js +1 -0
- package/apps/web/dist/assets/oss-credits-cZ9NUaLZ.js +1 -0
- package/apps/web/dist/assets/pdfmake-Cx9scTbD.js +228 -0
- package/apps/web/dist/assets/phone-CgrIS4_-.js +1 -0
- package/apps/web/dist/assets/preload-helper-CNEI7N0N.js +1 -0
- package/apps/web/dist/assets/qr-scanner-worker.min-CJaXo3yu.js +98 -0
- package/apps/web/dist/assets/qr-scanner.min-C_UI0gH-.js +1 -0
- package/apps/web/dist/assets/rolldown-runtime-hePW80VL.js +1 -0
- package/apps/web/dist/assets/settings-menu-2IMOOlLl.js +1 -0
- package/apps/web/dist/assets/ui-dzGvc3xk.js +137 -0
- package/apps/web/dist/assets/upload-BSuTilFF.js +1 -0
- package/apps/web/dist/assets/vfs_fonts-DllDSYt1.js +1 -0
- package/apps/web/dist/embed.js +266 -0
- package/apps/web/dist/favicon.svg +1 -0
- package/apps/web/dist/icons/apple-touch-icon.png +0 -0
- package/apps/web/dist/icons/icon-192.png +0 -0
- package/apps/web/dist/icons/icon-512.png +0 -0
- package/apps/web/dist/icons/icon-maskable-512.png +0 -0
- package/apps/web/dist/index.html +48 -0
- package/apps/web/dist/install.sh +577 -0
- package/apps/web/dist/manifest.webmanifest +21 -0
- package/apps/web/dist/mcp/flow.html +980 -0
- package/apps/web/dist/sw.js +15 -0
- package/bin/wizards.mjs +288 -0
- package/modules/README.md +15 -0
- package/node_modules/@engenty/wizards/dist/plugin/index.d.ts +1150 -0
- package/node_modules/@engenty/wizards/dist/plugin/index.js +4 -0
- package/node_modules/@engenty/wizards/dist/plugin/studio.d.ts +322 -0
- package/node_modules/@engenty/wizards/dist/plugin/studio.js +4 -0
- package/node_modules/@engenty/wizards/dist/plugin/thread.d.ts +174 -0
- package/node_modules/@engenty/wizards/dist/plugin/thread.js +821 -0
- package/node_modules/@engenty/wizards/package.json +11 -0
- package/node_modules/@engenty-wizards/shared/dist/assistant.d.ts +174 -0
- package/node_modules/@engenty-wizards/shared/dist/assistant.js +65 -0
- package/node_modules/@engenty-wizards/shared/dist/changes.d.ts +22 -0
- package/node_modules/@engenty-wizards/shared/dist/changes.js +61 -0
- package/node_modules/@engenty-wizards/shared/dist/connectors.d.ts +77 -0
- package/node_modules/@engenty-wizards/shared/dist/connectors.js +2 -0
- package/node_modules/@engenty-wizards/shared/dist/decision.d.ts +76 -0
- package/node_modules/@engenty-wizards/shared/dist/decision.js +147 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +2922 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.js +1920 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.d.ts +5 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.js +132 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.d.ts +11 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.js +209 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.d.ts +532 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.js +217 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.d.ts +3 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.js +3 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.d.ts +14 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.js +166 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.d.ts +33 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.js +99 -0
- package/node_modules/@engenty-wizards/shared/dist/feature-flags.d.ts +78 -0
- package/node_modules/@engenty-wizards/shared/dist/feature-flags.js +70 -0
- package/node_modules/@engenty-wizards/shared/dist/features.d.ts +41 -0
- package/node_modules/@engenty-wizards/shared/dist/features.js +89 -0
- package/node_modules/@engenty-wizards/shared/dist/fit.d.ts +71 -0
- package/node_modules/@engenty-wizards/shared/dist/fit.js +382 -0
- package/node_modules/@engenty-wizards/shared/dist/json-schema.d.ts +14 -0
- package/node_modules/@engenty-wizards/shared/dist/json-schema.js +66 -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-entry.d.ts +35 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.js +107 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-search.d.ts +20 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-search.js +136 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +227 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace.js +354 -0
- package/node_modules/@engenty-wizards/shared/dist/merge.d.ts +7 -0
- package/node_modules/@engenty-wizards/shared/dist/merge.js +40 -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/projects.d.ts +224 -0
- package/node_modules/@engenty-wizards/shared/dist/projects.js +319 -0
- package/node_modules/@engenty-wizards/shared/dist/providers.d.ts +101 -0
- package/node_modules/@engenty-wizards/shared/dist/providers.js +390 -0
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +680 -0
- package/node_modules/@engenty-wizards/shared/dist/run.js +620 -0
- package/node_modules/@engenty-wizards/shared/dist/runner-texts.d.ts +546 -0
- package/node_modules/@engenty-wizards/shared/dist/runner-texts.js +523 -0
- package/node_modules/@engenty-wizards/shared/dist/runners.d.ts +93 -0
- package/node_modules/@engenty-wizards/shared/dist/runners.js +146 -0
- package/node_modules/@engenty-wizards/shared/dist/shape.d.ts +102 -0
- package/node_modules/@engenty-wizards/shared/dist/shape.js +318 -0
- package/node_modules/@engenty-wizards/shared/dist/space-data.d.ts +89 -0
- package/node_modules/@engenty-wizards/shared/dist/space-data.js +47 -0
- package/node_modules/@engenty-wizards/shared/dist/store.d.ts +117 -0
- package/node_modules/@engenty-wizards/shared/dist/store.js +81 -0
- package/node_modules/@engenty-wizards/shared/dist/surface.d.ts +115 -0
- package/node_modules/@engenty-wizards/shared/dist/surface.js +442 -0
- package/node_modules/@engenty-wizards/shared/dist/texts.d.ts +45 -0
- package/node_modules/@engenty-wizards/shared/dist/texts.js +54 -0
- package/node_modules/@engenty-wizards/shared/dist/translation.d.ts +23 -0
- package/node_modules/@engenty-wizards/shared/dist/translation.js +154 -0
- package/node_modules/@engenty-wizards/shared/dist/wizard-package.d.ts +48 -0
- package/node_modules/@engenty-wizards/shared/dist/wizard-package.js +49 -0
- package/node_modules/@engenty-wizards/shared/dist/workspace.d.ts +16 -0
- package/node_modules/@engenty-wizards/shared/dist/workspace.js +51 -0
- package/node_modules/@engenty-wizards/shared/package.json +10 -0
- package/package.json +77 -4
- package/plugin/.claude-plugin/plugin.json +9 -0
- package/plugin/.mcp.json +8 -0
- package/plugin/commands/wizard.md +8 -0
- package/plugin/skills/build-wizard/SKILL.md +27 -0
- package/plugin/skills/use-wizard/SKILL.md +18 -0
|
@@ -0,0 +1,970 @@
|
|
|
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, textLang, whenHolds, wizardLang, } from "@engenty-wizards/shared/definition";
|
|
3
|
+
import { note, saidText, } from "@engenty-wizards/shared/run";
|
|
4
|
+
import { CONVERSATION_RUNNERS, DEFAULT_RUNNER_SETTINGS } from "@engenty-wizards/shared/runners";
|
|
5
|
+
import { fitData } from "@engenty-wizards/shared/shape";
|
|
6
|
+
import { machineTranslated, translateDefinition } from "@engenty-wizards/shared/translation";
|
|
7
|
+
import { eq } from "drizzle-orm";
|
|
8
|
+
import { nanoid } from "nanoid";
|
|
9
|
+
import { connectionViews } from "../connectors/index.js";
|
|
10
|
+
import { addLocalCost, canSpend, releaseRun, reserveForRun, syncRunCost, } from "../credits/credits.js";
|
|
11
|
+
import { estimateRun } from "../credits/estimate.js";
|
|
12
|
+
import { db, schema, withTenant } from "../db/client.js";
|
|
13
|
+
import { env } from "../env.js";
|
|
14
|
+
import { saveAsset } from "../files/storage.js";
|
|
15
|
+
import { managed, tenantInfo } from "../manage.js";
|
|
16
|
+
import { hasFfmpeg } from "../media/ffmpeg.js";
|
|
17
|
+
import { NotedError, noteOf, saidOf } from "../model-errors.js";
|
|
18
|
+
import { conversationAvailable, freeTierRefusal, freeTierText, ModelUnavailableError, noteFreeTierRefusal, } from "../models.js";
|
|
19
|
+
import { runEnded } from "../plugins/events.js";
|
|
20
|
+
import { projectFiles } from "../services/project-files.js";
|
|
21
|
+
import { projectProfile } from "../services/projects.js";
|
|
22
|
+
import { storedTranslation } from "../services/translations.js";
|
|
23
|
+
import { listRows, scopeOf } from "../store/index.js";
|
|
24
|
+
import { claimRun, disownRuns, heartbeatRuns, indexRun, restRun, waitingRuns, wakeRun, } from "../tenants/control.js";
|
|
25
|
+
import { currentTenant } from "../tenants/tenant.js";
|
|
26
|
+
import { askPerson, clearStaleAsk } from "./asks.js";
|
|
27
|
+
import { decide } from "./decide.js";
|
|
28
|
+
import { clearDeviceCalls } from "./device.js";
|
|
29
|
+
import { emitEvent, recentEvents, signalChanged } from "./events.js";
|
|
30
|
+
import { readPageInput } from "./input.js";
|
|
31
|
+
import { pushRun } from "./push.js";
|
|
32
|
+
import { blockingMessage, closedChoices, missingModelsSaid, openValue, stepsWithoutModel, } from "./requirements.js";
|
|
33
|
+
import { releaseResources, resourcesFor } from "./resources.js";
|
|
34
|
+
import { outputSchema, runAutomaticStep } from "./steps.js";
|
|
35
|
+
import { answersAsText, resolveRef } from "./template.js";
|
|
36
|
+
import { StepError } from "./types.js";
|
|
37
|
+
/** A command the run cannot take in its current state; a note where the person may read it. */
|
|
38
|
+
export class RunConflict extends NotedError {
|
|
39
|
+
}
|
|
40
|
+
export class RunInputError extends NotedError {
|
|
41
|
+
errors;
|
|
42
|
+
constructor(errors) {
|
|
43
|
+
super({ code: "checkFields" });
|
|
44
|
+
this.errors = errors;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export class NoCreditsError extends NotedError {
|
|
48
|
+
constructor() {
|
|
49
|
+
super(note("wizardRunOutCredits"));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** The runs this process drives right now, with the handle that stops each. */
|
|
53
|
+
const active = new Map();
|
|
54
|
+
// --- the line ------------------------------------------------------------------
|
|
55
|
+
// A run that wants work is marked in the control database (`wakeRun`); a runner claims it there,
|
|
56
|
+
// so several processes share one line. A tenant's runs beyond its limit stay in line; nothing is
|
|
57
|
+
// refused. See docs/plans/runner-process.md.
|
|
58
|
+
/** How often a runner looks at the line, and how often it reports on the runs it holds. */
|
|
59
|
+
const POLL_MS = 2000;
|
|
60
|
+
const HEARTBEAT_MS = 10_000;
|
|
61
|
+
async function concurrencyLimit(tenant) {
|
|
62
|
+
if (!managed) {
|
|
63
|
+
return env.limits.concurrentRuns;
|
|
64
|
+
}
|
|
65
|
+
// Out of reach, the Manage-App doesn't hold the line up: the runtime's own limit counts.
|
|
66
|
+
const info = await tenantInfo(tenant).catch((err) => {
|
|
67
|
+
console.warn(`[line] limit of tenant ${tenant}:`, err);
|
|
68
|
+
return null;
|
|
69
|
+
});
|
|
70
|
+
return info?.limits.concurrentRuns || env.limits.concurrentRuns;
|
|
71
|
+
}
|
|
72
|
+
async function loadRun(runId) {
|
|
73
|
+
return db.query.run.findFirst({ where: eq(schema.run.id, runId) });
|
|
74
|
+
}
|
|
75
|
+
/** The project a run's wizard belongs to — where its imported connectors live. */
|
|
76
|
+
export async function projectIdOf(run) {
|
|
77
|
+
const w = await db.query.wizard.findFirst({ where: eq(schema.wizard.id, run.wizardId) });
|
|
78
|
+
return w?.projectId ?? "";
|
|
79
|
+
}
|
|
80
|
+
async function loadProject(run) {
|
|
81
|
+
const w = await db.query.wizard.findFirst({ where: eq(schema.wizard.id, run.wizardId) });
|
|
82
|
+
const p = w && (await db.query.project.findFirst({ where: eq(schema.project.id, w.projectId) }));
|
|
83
|
+
if (!p) {
|
|
84
|
+
throw new Error("project missing");
|
|
85
|
+
}
|
|
86
|
+
return p;
|
|
87
|
+
}
|
|
88
|
+
async function updateRun(runId, patch) {
|
|
89
|
+
await db
|
|
90
|
+
.update(schema.run)
|
|
91
|
+
.set({ ...patch, updatedAt: new Date() })
|
|
92
|
+
.where(eq(schema.run.id, runId));
|
|
93
|
+
// A run that ended needs no credits held for it any more.
|
|
94
|
+
if (patch.status === "done" || patch.status === "failed" || patch.status === "cancelled") {
|
|
95
|
+
await releaseRun(runId);
|
|
96
|
+
// Plugins hear of it; the run does not wait for them.
|
|
97
|
+
void runEnded(runId, patch.status);
|
|
98
|
+
if (patch.status !== "cancelled") {
|
|
99
|
+
void pushRun(runId, patch.status);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
signalChanged(runId);
|
|
103
|
+
}
|
|
104
|
+
function stepOf(def, id) {
|
|
105
|
+
return id ? def.steps.find((s) => s.id === id) : undefined;
|
|
106
|
+
}
|
|
107
|
+
function friendly(err) {
|
|
108
|
+
if (err instanceof StepError || err instanceof ModelUnavailableError) {
|
|
109
|
+
return saidOf(err);
|
|
110
|
+
}
|
|
111
|
+
// An own AI Gateway key on Vercel's free tier: the person can top it up or pick another way.
|
|
112
|
+
const refused = freeTierRefusal(err);
|
|
113
|
+
if (refused !== null) {
|
|
114
|
+
return freeTierText(refused);
|
|
115
|
+
}
|
|
116
|
+
const msg = err?.message ?? String(err);
|
|
117
|
+
// The model-gateway answers 402 once the tenant's credits are used up.
|
|
118
|
+
if (/insufficient_credits|\b402\b/i.test(msg)) {
|
|
119
|
+
return saidOf(new NoCreditsError());
|
|
120
|
+
}
|
|
121
|
+
// A runtime that runs alone pays its provider itself: its admin can top the account up.
|
|
122
|
+
if (!managed && /minimum balance|insufficient_funds|top up your credits/i.test(msg)) {
|
|
123
|
+
return note("providerCreditLow");
|
|
124
|
+
}
|
|
125
|
+
if (/rate.?limit|429/i.test(msg)) {
|
|
126
|
+
return note("aiServiceBusyRight");
|
|
127
|
+
}
|
|
128
|
+
if (/timeout|timed out/i.test(msg)) {
|
|
129
|
+
return note("stepTookTooLong");
|
|
130
|
+
}
|
|
131
|
+
// The platform's own provider account (budget, quota, key): nothing the person can fix.
|
|
132
|
+
if (/budget|quota|insufficient|billing|credit balance|api key|unauthori[sz]ed|401|403/i.test(msg)) {
|
|
133
|
+
return note("aiServiceNotReached");
|
|
134
|
+
}
|
|
135
|
+
return note("stepFailed", { detail: msg.slice(0, 300) });
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* A step that failed ends the run. A failure with a note (Apple Intelligence's) goes into the log
|
|
139
|
+
* as that note, so a runner shows it in its language; the run's error is in the wizard's.
|
|
140
|
+
*/
|
|
141
|
+
async function failRun(run, stepId, err) {
|
|
142
|
+
const said = noteOf(err) ?? friendly(err);
|
|
143
|
+
const message = saidText(said, textLang(wizardLang(run.definition)));
|
|
144
|
+
await emitEvent(run.id, stepId, "error", said);
|
|
145
|
+
await updateRun(run.id, { status: "failed", error: message });
|
|
146
|
+
}
|
|
147
|
+
// --- creating & driving ------------------------------------------------------
|
|
148
|
+
export async function createRun(input) {
|
|
149
|
+
const id = nanoid(18);
|
|
150
|
+
const first = input.definition.steps[0];
|
|
151
|
+
const state = { values: {}, outputs: {}, history: [], notes: {} };
|
|
152
|
+
// A step every run passes through needs a model this runtime cannot serve: refuse before anything is held.
|
|
153
|
+
const blocked = blockingMessage(await missingModelsSaid(input.definition));
|
|
154
|
+
if (blocked) {
|
|
155
|
+
throw new ModelUnavailableError(blocked);
|
|
156
|
+
}
|
|
157
|
+
// The run's expected cost is held before it starts; what it really costs is booked per call.
|
|
158
|
+
const estimate = await estimateRun(input.wizardId, input.version, input.definition, await stepsWithoutModel(input.definition));
|
|
159
|
+
if (!(await reserveForRun(id, estimate.reserve))) {
|
|
160
|
+
throw new NoCreditsError();
|
|
161
|
+
}
|
|
162
|
+
await db.insert(schema.run).values({
|
|
163
|
+
id,
|
|
164
|
+
wizardId: input.wizardId,
|
|
165
|
+
tenantId: currentTenant(),
|
|
166
|
+
definition: input.definition,
|
|
167
|
+
files: input.files,
|
|
168
|
+
version: input.version,
|
|
169
|
+
mode: input.mode,
|
|
170
|
+
runner: input.runner ?? null,
|
|
171
|
+
visitorId: input.visitorId ?? null,
|
|
172
|
+
userId: input.userId ?? null,
|
|
173
|
+
ipHash: input.ipHash ?? null,
|
|
174
|
+
origin: input.origin ?? null,
|
|
175
|
+
lang: input.lang ?? null,
|
|
176
|
+
unattended: input.unattended ?? false,
|
|
177
|
+
status: first.type === "page" ? "waiting_input" : "running",
|
|
178
|
+
cursor: first.id,
|
|
179
|
+
state,
|
|
180
|
+
// Without an account nobody can come back for a result later: it is kept a week.
|
|
181
|
+
expiresAt: input.mode === "live"
|
|
182
|
+
? new Date(Date.now() + env.limits.resultTtlDays * 24 * 60 * 60 * 1000)
|
|
183
|
+
: null,
|
|
184
|
+
});
|
|
185
|
+
await indexRun({ runId: id, visitorId: input.visitorId, ipHash: input.ipHash });
|
|
186
|
+
if (first.type !== "page") {
|
|
187
|
+
kick(id);
|
|
188
|
+
}
|
|
189
|
+
return id;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Puts the run to work: in line in the control database, and — when this process is a runner —
|
|
193
|
+
* taken at once if the tenant has a slot. The web alone leaves it for a runner's next look.
|
|
194
|
+
*/
|
|
195
|
+
export function kick(runId) {
|
|
196
|
+
const tenant = currentTenant();
|
|
197
|
+
void wakeRun(runId)
|
|
198
|
+
.then(() => {
|
|
199
|
+
if (env.role !== "web") {
|
|
200
|
+
return take(runId, tenant);
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
.catch((err) => console.error(`[run ${runId}]`, err));
|
|
204
|
+
}
|
|
205
|
+
/** Claims the run for this process and drives it; nothing when another holds it or the tenant is full. */
|
|
206
|
+
async function take(runId, tenant) {
|
|
207
|
+
if (active.has(runId)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (!(await claimRun(runId, tenant, await concurrencyLimit(tenant)))) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const abort = new AbortController();
|
|
214
|
+
active.set(runId, abort);
|
|
215
|
+
// The heartbeat keeps the claim and hears of a cancel the web wrote while a step works.
|
|
216
|
+
const beat = setInterval(() => {
|
|
217
|
+
void heartbeatRuns([runId]).catch((err) => console.warn(`[run ${runId}] heartbeat:`, err));
|
|
218
|
+
void withTenant(tenant, () => loadRun(runId))
|
|
219
|
+
.then((run) => {
|
|
220
|
+
if (run && run.status !== "running") {
|
|
221
|
+
abort.abort();
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
.catch((err) => console.warn(`[run ${runId}] cancel check:`, err));
|
|
225
|
+
}, HEARTBEAT_MS);
|
|
226
|
+
beat.unref();
|
|
227
|
+
await withTenant(tenant, () => drive(runId, abort.signal))
|
|
228
|
+
.catch(async (err) => {
|
|
229
|
+
console.error(`[run ${runId}]`, err);
|
|
230
|
+
await withTenant(tenant, async () => {
|
|
231
|
+
const run = await loadRun(runId);
|
|
232
|
+
const lang = run ? textLang(wizardLang(run.definition)) : "en";
|
|
233
|
+
await updateRun(runId, { status: "failed", error: saidText(friendly(err), lang) });
|
|
234
|
+
}).catch((e) => console.error(`[run ${runId}] marking it failed:`, e));
|
|
235
|
+
})
|
|
236
|
+
.finally(async () => {
|
|
237
|
+
clearInterval(beat);
|
|
238
|
+
active.delete(runId);
|
|
239
|
+
// What fails here leaves the rest of the cleanup to run.
|
|
240
|
+
await withTenant(tenant, () => releaseResources(runId)).catch((err) => console.error(`[run ${runId}] releasing its resources:`, err));
|
|
241
|
+
// A run the shutdown stopped goes back in line for the next runner; the rest is done for now.
|
|
242
|
+
if (!stopping) {
|
|
243
|
+
await restRun(runId).catch((err) => console.error(`[run ${runId}] rest:`, err));
|
|
244
|
+
}
|
|
245
|
+
void withTenant(tenant, () => signalChanged(runId)).catch((err) => console.error(`[run ${runId}] signal:`, err));
|
|
246
|
+
// A slot came free: the next run of the line need not wait for the poll.
|
|
247
|
+
void lookAtLine();
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
let stopping = false;
|
|
251
|
+
let looking = false;
|
|
252
|
+
/** Takes what waits in line, run by run; each claim counts the tenant's limit by itself. */
|
|
253
|
+
async function lookAtLine() {
|
|
254
|
+
if (looking || stopping) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
looking = true;
|
|
258
|
+
try {
|
|
259
|
+
for (const r of await waitingRuns()) {
|
|
260
|
+
if (stopping) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
await take(r.runId, r.tenantId).catch((err) => console.error(`[line ${r.runId}]`, err));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
console.error("[line]", err);
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
looking = false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/** A runner: looks at the line every POLL_MS, from now on. Runs a stopped process left behind are in it. */
|
|
274
|
+
export function startRunner() {
|
|
275
|
+
const timer = setInterval(() => void lookAtLine(), POLL_MS);
|
|
276
|
+
timer.unref();
|
|
277
|
+
void lookAtLine();
|
|
278
|
+
}
|
|
279
|
+
/** Stops the runs this process holds and hands them back to the line. */
|
|
280
|
+
export async function stopRunner() {
|
|
281
|
+
stopping = true;
|
|
282
|
+
for (const abort of active.values()) {
|
|
283
|
+
abort.abort();
|
|
284
|
+
}
|
|
285
|
+
await disownRuns().catch(() => undefined);
|
|
286
|
+
}
|
|
287
|
+
/** What conditions read in this run: answers, outputs, and how many rows each stored list has. */
|
|
288
|
+
async function flowValues(run, state) {
|
|
289
|
+
const counts = {};
|
|
290
|
+
if (run.definition.lists?.length) {
|
|
291
|
+
for (const [id, list] of Object.entries(await storedLists(run))) {
|
|
292
|
+
counts[id] = list.rows.length;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return branchValues(state.values, state.outputs, counts);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* What a page knows besides the person's input: what its fields' `when` read from before the
|
|
299
|
+
* page (its own earlier answers left out, so a revisit decides on what is typed now) and the
|
|
300
|
+
* choices its `optionsFrom` fields find in the run.
|
|
301
|
+
*/
|
|
302
|
+
export async function pageContext(run, step) {
|
|
303
|
+
const needs = pageNeeds(step.fields);
|
|
304
|
+
const sourced = step.fields.filter((f) => f.optionsFrom);
|
|
305
|
+
const decided = step.fields.some(isDecidedField) ? (run.state.pages?.[step.id]?.shown ?? []) : [];
|
|
306
|
+
if (!needs.length && !sourced.length) {
|
|
307
|
+
return { known: {}, options: {}, cards: {}, decided };
|
|
308
|
+
}
|
|
309
|
+
const all = await flowValues(run, run.state);
|
|
310
|
+
const known = Object.fromEntries(needs.map((ref) => [ref, all[ref]]));
|
|
311
|
+
const lists = sourced.some((f) => f.optionsFrom?.startsWith("lists."))
|
|
312
|
+
? await storedLists(run)
|
|
313
|
+
: {};
|
|
314
|
+
const options = {};
|
|
315
|
+
const cards = {};
|
|
316
|
+
for (const field of sourced) {
|
|
317
|
+
const found = optionsFromData(field.optionsFrom ?? "", run.state.outputs, lists, field.cards);
|
|
318
|
+
if (found.length) {
|
|
319
|
+
options[field.id] = found;
|
|
320
|
+
}
|
|
321
|
+
if (field.cards) {
|
|
322
|
+
const drawn = cardsFromData(field.optionsFrom ?? "", run.state.outputs, field.cards);
|
|
323
|
+
if (drawn.length) {
|
|
324
|
+
cards[field.id] = drawn;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return { known, options, cards, decided };
|
|
329
|
+
}
|
|
330
|
+
function viewContext(ctx) {
|
|
331
|
+
return { known: ctx.known, options: ctx.options, cards: ctx.cards, decidedFields: ctx.decided };
|
|
332
|
+
}
|
|
333
|
+
/** The wizard's lists with the person's rows, by list id. */
|
|
334
|
+
async function storedLists(run) {
|
|
335
|
+
const scope = scopeOf(run);
|
|
336
|
+
const out = {};
|
|
337
|
+
for (const def of run.definition.lists ?? []) {
|
|
338
|
+
out[def.id] = { def, rows: await listRows(scope, def) };
|
|
339
|
+
}
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
async function makeContext(run, project, state, signal) {
|
|
343
|
+
const store = scopeOf(run);
|
|
344
|
+
const resources = resourcesFor(run.id);
|
|
345
|
+
resources.bind(store);
|
|
346
|
+
const stepId = run.cursor ?? "";
|
|
347
|
+
return {
|
|
348
|
+
runId: run.id,
|
|
349
|
+
tenantId: run.tenantId,
|
|
350
|
+
test: run.mode === "test",
|
|
351
|
+
stepId,
|
|
352
|
+
store,
|
|
353
|
+
def: run.definition,
|
|
354
|
+
lang: run.lang,
|
|
355
|
+
files: run.files,
|
|
356
|
+
state,
|
|
357
|
+
scope: {
|
|
358
|
+
def: run.definition,
|
|
359
|
+
state,
|
|
360
|
+
brand: projectProfile(project),
|
|
361
|
+
lists: await storedLists(run),
|
|
362
|
+
},
|
|
363
|
+
project,
|
|
364
|
+
projectFiles: (await projectFiles(project.id)).filter((f) => f.status === "ready"),
|
|
365
|
+
signal,
|
|
366
|
+
resources,
|
|
367
|
+
ask: (ask) => run.unattended ? Promise.resolve(null) : askPerson(run.id, { ...ask, stepId }, signal),
|
|
368
|
+
emit: (type, message, asset) => emitEvent(run.id, run.cursor, type, message, asset),
|
|
369
|
+
call: { runId: run.id, stepId },
|
|
370
|
+
chargeUsd: (usd) => addLocalCost(run.id, stepId, usd),
|
|
371
|
+
saveAsset: (input) => saveAsset({
|
|
372
|
+
...input,
|
|
373
|
+
runId: run.id,
|
|
374
|
+
stepId: input.stepId ?? run.cursor,
|
|
375
|
+
}),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/** A decided branch is taken only when its pick leads the runner-up by this much. */
|
|
379
|
+
const BRANCH_MARGIN = 0.1;
|
|
380
|
+
/**
|
|
381
|
+
* Where the run goes after `stepId`: a `when` rule or the next step at once — counted, so a loop
|
|
382
|
+
* stops at its rule's max — or, for `ask` rules, a decision the drive loop makes (`deciding`).
|
|
383
|
+
*/
|
|
384
|
+
async function advance(run, stepId, state) {
|
|
385
|
+
const outcome = followRules(run.definition, stepId, await flowValues(run, state), state.loops);
|
|
386
|
+
if (outcome.kind === "decide") {
|
|
387
|
+
state.deciding = stepId;
|
|
388
|
+
return stepId;
|
|
389
|
+
}
|
|
390
|
+
return taken(run.definition, stepId, outcome.rule, outcome.cursor, state);
|
|
391
|
+
}
|
|
392
|
+
function taken(def, stepId, rule, cursor, state) {
|
|
393
|
+
const goto = rule === null ? null : stepOf(def, stepId)?.next?.[rule]?.goto;
|
|
394
|
+
if (goto) {
|
|
395
|
+
const key = loopKey(stepId, goto);
|
|
396
|
+
state.loops = { ...state.loops, [key]: (state.loops?.[key] ?? 0) + 1 };
|
|
397
|
+
}
|
|
398
|
+
return cursor;
|
|
399
|
+
}
|
|
400
|
+
/** What a decision about the run reads: the person's answers and what the steps made so far. */
|
|
401
|
+
function runSummary(run, state) {
|
|
402
|
+
const results = {};
|
|
403
|
+
for (const step of run.definition.steps) {
|
|
404
|
+
const out = state.outputs[step.id];
|
|
405
|
+
const text = out?.text ?? (out?.json === undefined ? "" : JSON.stringify(out.json));
|
|
406
|
+
if (text) {
|
|
407
|
+
results[step.title] = text.slice(0, 6000);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
answers: answersAsText({ def: run.definition, state, brand: {} }),
|
|
412
|
+
results,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* The decision over a step's open `ask` rules: one choice among their statements and "none".
|
|
417
|
+
* Kept in the run; a pick that does not lead clearly (where there are probabilities) is none.
|
|
418
|
+
*/
|
|
419
|
+
async function decideBranch(run, step, state, signal) {
|
|
420
|
+
const outcome = followRules(run.definition, step.id, {}, state.loops);
|
|
421
|
+
const rules = outcome.kind === "decide" ? outcome.rules : [];
|
|
422
|
+
const criteria = {};
|
|
423
|
+
for (const i of rules) {
|
|
424
|
+
criteria[`r${i}`] = step.next?.[i]?.ask ?? "";
|
|
425
|
+
}
|
|
426
|
+
criteria.none = "None of these clearly holds: go on with the next step.";
|
|
427
|
+
await emitEvent(run.id, step.id, "info", { code: "deciding" });
|
|
428
|
+
const decision = await decide({
|
|
429
|
+
state: runSummary(run, state),
|
|
430
|
+
questions: {
|
|
431
|
+
next: {
|
|
432
|
+
type: "choice",
|
|
433
|
+
instructions: "Which statement is true of this run? Answer none unless one clearly holds.",
|
|
434
|
+
criteria,
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
call: { runId: run.id, stepId: step.id },
|
|
438
|
+
signal,
|
|
439
|
+
charge: (usd) => addLocalCost(run.id, step.id, usd),
|
|
440
|
+
});
|
|
441
|
+
const answer = decision.answers.next;
|
|
442
|
+
const margin = marginOf(answer);
|
|
443
|
+
const pick = margin !== null && margin < BRANCH_MARGIN ? "none" : answer.choice;
|
|
444
|
+
const rule = pick === "none" ? null : Number(pick.slice(1));
|
|
445
|
+
state.decisions = {
|
|
446
|
+
...state.decisions,
|
|
447
|
+
[step.id]: {
|
|
448
|
+
rule,
|
|
449
|
+
source: decision.source,
|
|
450
|
+
...(answer.probabilities ? { probabilities: answer.probabilities } : {}),
|
|
451
|
+
at: new Date().toISOString(),
|
|
452
|
+
},
|
|
453
|
+
};
|
|
454
|
+
delete state.deciding;
|
|
455
|
+
if (rule === null) {
|
|
456
|
+
const otherwise = otherwiseOf(run.definition, step.id, state.loops);
|
|
457
|
+
return taken(run.definition, step.id, otherwise.rule, otherwise.cursor, state);
|
|
458
|
+
}
|
|
459
|
+
const goto = step.next?.[rule]?.goto ?? "end";
|
|
460
|
+
return taken(run.definition, step.id, rule, goto === "end" ? null : goto, state);
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* The decided fields (`ask`, `group`) of a page a decision keeps, in one call as the run reaches
|
|
464
|
+
* the page. A field whose condition on earlier data already fails is not asked about. With the
|
|
465
|
+
* fields always shown, at most MAX_SHOWN_FIELDS appear.
|
|
466
|
+
*/
|
|
467
|
+
async function decidePageFields(run, page, state, signal) {
|
|
468
|
+
const values = await flowValues(run, state);
|
|
469
|
+
const own = new Set(page.fields.map((f) => f.id));
|
|
470
|
+
const possible = page.fields.filter((f) => isDecidedField(f) &&
|
|
471
|
+
whenHolds(conditionsOf(f.when).filter((c) => !own.has(c.field)), values));
|
|
472
|
+
const questions = {};
|
|
473
|
+
for (const f of possible.filter((f) => f.ask)) {
|
|
474
|
+
questions[`f_${f.id}`] = {
|
|
475
|
+
type: "noul",
|
|
476
|
+
instructions: `Should the page ask "${f.label}"? It is asked when this holds: ${f.ask}`,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
for (const g of page.groups ?? []) {
|
|
480
|
+
const members = possible.filter((f) => f.group === g.id);
|
|
481
|
+
if (members.length) {
|
|
482
|
+
questions[`g_${g.id}`] = {
|
|
483
|
+
type: "choice",
|
|
484
|
+
instructions: g.instructions,
|
|
485
|
+
criteria: {
|
|
486
|
+
...Object.fromEntries(members.map((f) => [f.id, f.help ? `${f.label} — ${f.help}` : f.label])),
|
|
487
|
+
...(g.optional ? { none: "None of these." } : {}),
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (!Object.keys(questions).length) {
|
|
493
|
+
return [];
|
|
494
|
+
}
|
|
495
|
+
await emitEvent(run.id, page.id, "info", { code: "choosingFields" });
|
|
496
|
+
const decision = await decide({
|
|
497
|
+
state: runSummary(run, state),
|
|
498
|
+
questions,
|
|
499
|
+
call: { runId: run.id, stepId: page.id },
|
|
500
|
+
signal,
|
|
501
|
+
charge: (usd) => addLocalCost(run.id, page.id, usd),
|
|
502
|
+
});
|
|
503
|
+
const kept = new Set();
|
|
504
|
+
for (const [name, answer] of Object.entries(decision.answers)) {
|
|
505
|
+
if (answer.type === "noul" && answer.noul >= 0.5) {
|
|
506
|
+
kept.add(name.slice(2));
|
|
507
|
+
}
|
|
508
|
+
else if (answer.type === "choice" && answer.choice !== "none") {
|
|
509
|
+
kept.add(answer.choice);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const room = MAX_SHOWN_FIELDS - page.fields.filter((f) => !isDecidedField(f)).length;
|
|
513
|
+
return page.fields
|
|
514
|
+
.filter((f) => isDecidedField(f) && kept.has(f.id))
|
|
515
|
+
.slice(0, Math.max(0, room))
|
|
516
|
+
.map((f) => f.id);
|
|
517
|
+
}
|
|
518
|
+
async function drive(runId, signal) {
|
|
519
|
+
// The step starts afresh here: a question or a device call another process left has no one
|
|
520
|
+
// waiting.
|
|
521
|
+
await clearStaleAsk(runId);
|
|
522
|
+
await clearDeviceCalls(runId);
|
|
523
|
+
while (!signal.aborted) {
|
|
524
|
+
const run = await loadRun(runId);
|
|
525
|
+
if (run?.status !== "running") {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
const def = run.definition;
|
|
529
|
+
// A step's `ask` branches wait on a decision before the run goes on.
|
|
530
|
+
const deciding = run.state.deciding ? stepOf(def, run.state.deciding) : undefined;
|
|
531
|
+
if (deciding) {
|
|
532
|
+
const state = structuredClone(run.state);
|
|
533
|
+
try {
|
|
534
|
+
const cursor = await decideBranch(run, deciding, state, signal);
|
|
535
|
+
if (signal.aborted) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
await syncRunCost(runId);
|
|
539
|
+
await updateRun(runId, { state, cursor, status: cursor ? "running" : "done", error: null });
|
|
540
|
+
}
|
|
541
|
+
catch (err) {
|
|
542
|
+
if (signal.aborted) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
console.error(`[run ${runId} deciding ${deciding.id}]`, err);
|
|
546
|
+
await failRun(run, deciding.id, err);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
const step = stepOf(def, run.cursor);
|
|
552
|
+
if (!step) {
|
|
553
|
+
await updateRun(runId, { status: "done" });
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
// A page whose fields a decision picks: picked once, as the run reaches it.
|
|
557
|
+
if (step.type === "page" && step.fields.some(isDecidedField) && !run.state.pages?.[step.id]) {
|
|
558
|
+
const state = structuredClone(run.state);
|
|
559
|
+
try {
|
|
560
|
+
const shown = await decidePageFields(run, step, state, signal);
|
|
561
|
+
if (signal.aborted) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
state.pages = { ...state.pages, [step.id]: { shown } };
|
|
565
|
+
await syncRunCost(runId);
|
|
566
|
+
await updateRun(runId, { state, error: null });
|
|
567
|
+
}
|
|
568
|
+
catch (err) {
|
|
569
|
+
if (signal.aborted) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
console.error(`[run ${runId} fields of ${step.id}]`, err);
|
|
573
|
+
await failRun(run, step.id, err);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (step.type === "result") {
|
|
579
|
+
await updateRun(runId, { status: "done" });
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
if (step.type === "page" || step.type === "review") {
|
|
583
|
+
await updateRun(runId, { status: "waiting_input" });
|
|
584
|
+
// A step that ran is over and the next one is the person's: a phone in a pocket hears it.
|
|
585
|
+
void pushRun(runId, "waiting");
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (!(await canSpend())) {
|
|
589
|
+
const message = new NoCreditsError().message;
|
|
590
|
+
await emitEvent(runId, step.id, "error", message);
|
|
591
|
+
await updateRun(runId, { status: "failed", error: message });
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
await emitEvent(runId, step.id, "step_started", step.working ?? step.title);
|
|
595
|
+
const project = await loadProject(run);
|
|
596
|
+
const state = structuredClone(run.state);
|
|
597
|
+
try {
|
|
598
|
+
const output = await runAutomaticStep(step, await makeContext(run, project, state, signal));
|
|
599
|
+
if (signal.aborted) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
await syncRunCost(runId);
|
|
603
|
+
state.outputs[step.id] = output;
|
|
604
|
+
delete state.notes[step.id];
|
|
605
|
+
delete state.redo?.[step.id];
|
|
606
|
+
const cursor = await advance(run, step.id, state);
|
|
607
|
+
await updateRun(runId, { state, cursor, status: cursor ? "running" : "done", error: null });
|
|
608
|
+
await emitEvent(runId, step.id, "step_done", step.title);
|
|
609
|
+
}
|
|
610
|
+
catch (err) {
|
|
611
|
+
if (signal.aborted) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
console.error(`[run ${runId} step ${step.id}]`, err);
|
|
615
|
+
await noteFreeTierRefusal(err);
|
|
616
|
+
await failRun(run, step.id, err);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
// --- commands ----------------------------------------------------------------
|
|
622
|
+
async function requireWaiting(runId, stepId) {
|
|
623
|
+
const run = await loadRun(runId);
|
|
624
|
+
if (!run) {
|
|
625
|
+
throw new RunConflict("Run not found");
|
|
626
|
+
}
|
|
627
|
+
if (run.status !== "waiting_input" && run.status !== "done") {
|
|
628
|
+
throw new RunConflict({ code: "runBusy" });
|
|
629
|
+
}
|
|
630
|
+
if (run.cursor !== stepId) {
|
|
631
|
+
throw new RunConflict({ code: "pageOutdated" });
|
|
632
|
+
}
|
|
633
|
+
return run;
|
|
634
|
+
}
|
|
635
|
+
export async function submitPage(runId, stepId, input) {
|
|
636
|
+
const run = await requireWaiting(runId, stepId);
|
|
637
|
+
const step = stepOf(run.definition, stepId);
|
|
638
|
+
if (step?.type !== "page") {
|
|
639
|
+
throw new RunConflict("Not a page");
|
|
640
|
+
}
|
|
641
|
+
const { values, errors } = readPageInput(step, input, await pageContext(run, step));
|
|
642
|
+
// An answer that leads to a step without a model here would stop the run there, later.
|
|
643
|
+
const closed = await closedChoices(run.definition, step, run.state.values);
|
|
644
|
+
for (const [id, choice] of Object.entries(closed)) {
|
|
645
|
+
const answer = values[id];
|
|
646
|
+
if (answer !== undefined &&
|
|
647
|
+
(choice.values.length === 0 || choice.values.some((v) => String(v) === String(answer)))) {
|
|
648
|
+
errors.push({
|
|
649
|
+
field: id,
|
|
650
|
+
message: note("notAvailableNow"),
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
if (errors.length) {
|
|
655
|
+
throw new RunInputError(errors);
|
|
656
|
+
}
|
|
657
|
+
const connections = step.fields.some((f) => f.kind === "connection")
|
|
658
|
+
? await connectionViews(run.definition.connections ?? [], scopeOf(run), await projectIdOf(run), textLang(wizardLang(run.definition)))
|
|
659
|
+
: [];
|
|
660
|
+
for (const field of step.fields) {
|
|
661
|
+
if (field.kind === "image" || field.kind === "file") {
|
|
662
|
+
const value = values[field.id];
|
|
663
|
+
for (const id of Array.isArray(value) ? value : value ? [value] : []) {
|
|
664
|
+
const asset = await db.query.asset.findFirst({ where: eq(schema.asset.id, String(id)) });
|
|
665
|
+
if (asset?.runId !== runId) {
|
|
666
|
+
throw new RunInputError([{ field: field.id, message: note("fileNotFound") }]);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (field.kind === "connection") {
|
|
671
|
+
// What the step later reads as {{field}} is the connected account, as the server knows it.
|
|
672
|
+
const account = connections.find((c) => c.id === field.connection)?.account;
|
|
673
|
+
if (account) {
|
|
674
|
+
values[field.id] = account.label;
|
|
675
|
+
}
|
|
676
|
+
else if (field.required) {
|
|
677
|
+
throw new RunInputError([{ field: field.id, message: note("connectFirst") }]);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
const state = structuredClone(run.state);
|
|
682
|
+
state.values = { ...state.values, ...values };
|
|
683
|
+
// Fields left empty on a revisited page are cleared, not kept from before.
|
|
684
|
+
for (const field of step.fields) {
|
|
685
|
+
if (!(field.id in values)) {
|
|
686
|
+
delete state.values[field.id];
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
// Later pages decide their fields again on what is answered now.
|
|
690
|
+
const at = run.definition.steps.indexOf(step);
|
|
691
|
+
for (const later of run.definition.steps.slice(at + 1)) {
|
|
692
|
+
delete state.pages?.[later.id];
|
|
693
|
+
}
|
|
694
|
+
state.history.push(stepId);
|
|
695
|
+
const cursor = await advance(run, stepId, state);
|
|
696
|
+
await updateRun(runId, { state, cursor, status: "running", error: null });
|
|
697
|
+
kick(runId);
|
|
698
|
+
}
|
|
699
|
+
/** Edited data of a json step is kept up to this size. */
|
|
700
|
+
const MAX_EDITED_JSON = 500_000;
|
|
701
|
+
/**
|
|
702
|
+
* A json step's output with data someone edited: checked against the step's schema, and what
|
|
703
|
+
* its view shows too. Refused when it does not fit.
|
|
704
|
+
*/
|
|
705
|
+
export function editedJson(def, stepId, output, data) {
|
|
706
|
+
const step = stepOf(def, stepId);
|
|
707
|
+
if (step?.type !== "agent" || step.output.format !== "json" || step.each || step.call) {
|
|
708
|
+
throw new RunConflict("Not a json step");
|
|
709
|
+
}
|
|
710
|
+
if (JSON.stringify(data).length > MAX_EDITED_JSON) {
|
|
711
|
+
throw new RunConflict("Too large");
|
|
712
|
+
}
|
|
713
|
+
const { schema, shape } = outputSchema(step);
|
|
714
|
+
const parsed = schema.safeParse(fitData(shape, data));
|
|
715
|
+
if (!parsed.success) {
|
|
716
|
+
const issue = parsed.error.issues[0];
|
|
717
|
+
throw new RunConflict(`Does not fit the schema: ${issue?.path.join(".")} ${issue?.message}`);
|
|
718
|
+
}
|
|
719
|
+
const json = parsed.data;
|
|
720
|
+
return {
|
|
721
|
+
...output,
|
|
722
|
+
json,
|
|
723
|
+
...(output.surface ? { surface: { ...output.surface, data: json } } : {}),
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
/** Data of a finished or waiting run's json step, edited in the studio. */
|
|
727
|
+
export async function editOutput(runId, stepId, data) {
|
|
728
|
+
const run = await loadRun(runId);
|
|
729
|
+
const output = run?.state.outputs[stepId];
|
|
730
|
+
if (!run || !output) {
|
|
731
|
+
throw new RunConflict("Unknown step");
|
|
732
|
+
}
|
|
733
|
+
if (run.status !== "waiting_input" && run.status !== "done") {
|
|
734
|
+
throw new RunConflict({ code: "runBusy" });
|
|
735
|
+
}
|
|
736
|
+
const state = structuredClone(run.state);
|
|
737
|
+
state.outputs[stepId] = editedJson(run.definition, stepId, output, data);
|
|
738
|
+
await updateRun(runId, { state });
|
|
739
|
+
}
|
|
740
|
+
export async function reviewStep(runId, stepId, action) {
|
|
741
|
+
const run = await requireWaiting(runId, stepId);
|
|
742
|
+
const step = stepOf(run.definition, stepId);
|
|
743
|
+
if (step?.type !== "review") {
|
|
744
|
+
throw new RunConflict("Not a review");
|
|
745
|
+
}
|
|
746
|
+
const state = structuredClone(run.state);
|
|
747
|
+
if (action.type === "accept") {
|
|
748
|
+
for (const [target, edit] of Object.entries(action.edits ?? {})) {
|
|
749
|
+
const output = state.outputs[target];
|
|
750
|
+
if (!step.edit || !step.show.includes(target) || !output) {
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
if (typeof edit === "string") {
|
|
754
|
+
state.outputs[target] = { ...output, text: edit.slice(0, 50_000) };
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
state.outputs[target] = editedJson(run.definition, target, output, edit);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
state.history.push(stepId);
|
|
761
|
+
const cursor = await advance(run, stepId, state);
|
|
762
|
+
await updateRun(runId, { state, cursor, status: "running", error: null });
|
|
763
|
+
kick(runId);
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
if (!step.show.includes(action.target)) {
|
|
767
|
+
throw new RunConflict("Unknown target");
|
|
768
|
+
}
|
|
769
|
+
state.notes[action.target] = action.note.slice(0, 2000) || "Please make a new variant.";
|
|
770
|
+
// A step with several results makes only the named ones again.
|
|
771
|
+
if (action.items?.length) {
|
|
772
|
+
state.redo = { ...state.redo, [action.target]: action.items };
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
delete state.redo?.[action.target];
|
|
776
|
+
}
|
|
777
|
+
await updateRun(runId, { state, cursor: action.target, status: "running", error: null });
|
|
778
|
+
await emitEvent(runId, action.target, "info", "Wird neu erstellt …");
|
|
779
|
+
kick(runId);
|
|
780
|
+
}
|
|
781
|
+
export async function goBack(runId) {
|
|
782
|
+
const run = await loadRun(runId);
|
|
783
|
+
if (!run ||
|
|
784
|
+
(run.status !== "waiting_input" && run.status !== "done" && run.status !== "failed")) {
|
|
785
|
+
throw new RunConflict({ code: "runBusy" });
|
|
786
|
+
}
|
|
787
|
+
const state = structuredClone(run.state);
|
|
788
|
+
const previous = state.history.pop();
|
|
789
|
+
if (!previous) {
|
|
790
|
+
throw new RunConflict({ code: "noStepBefore" });
|
|
791
|
+
}
|
|
792
|
+
// A decision that failed belongs to the step left behind.
|
|
793
|
+
delete state.deciding;
|
|
794
|
+
await updateRun(runId, { state, cursor: previous, status: "waiting_input", error: null });
|
|
795
|
+
}
|
|
796
|
+
export async function retry(runId) {
|
|
797
|
+
const run = await loadRun(runId);
|
|
798
|
+
if (run?.status !== "failed") {
|
|
799
|
+
throw new RunConflict("Nothing to retry");
|
|
800
|
+
}
|
|
801
|
+
await updateRun(runId, { status: "running", error: null });
|
|
802
|
+
kick(runId);
|
|
803
|
+
}
|
|
804
|
+
export async function cancel(runId) {
|
|
805
|
+
active.get(runId)?.abort();
|
|
806
|
+
await updateRun(runId, { status: "cancelled" });
|
|
807
|
+
}
|
|
808
|
+
// --- view --------------------------------------------------------------------
|
|
809
|
+
/** The formats a deliverable can really be downloaded in: a still widget has no video. */
|
|
810
|
+
export async function availableFormats(step, run, wanted) {
|
|
811
|
+
const possible = formatsFor(step);
|
|
812
|
+
let formats = wanted ? wanted.filter((f) => possible.includes(f)) : possible;
|
|
813
|
+
const output = run.state.outputs[step.id];
|
|
814
|
+
if (step.type === "widget" && formats.includes("mp4")) {
|
|
815
|
+
// A film is rendered when its step runs; any other widget on demand.
|
|
816
|
+
const has = step.video
|
|
817
|
+
? output?.assets?.some((a) => a.kind === "video")
|
|
818
|
+
: output?.widget?.duration && (await hasFfmpeg());
|
|
819
|
+
if (!has) {
|
|
820
|
+
formats = formats.filter((f) => f !== "mp4");
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
// A zip holds the files a step collected, or its several results; with one there is nothing to zip.
|
|
824
|
+
if (formats.includes("zip")) {
|
|
825
|
+
const has = step.type === "generate"
|
|
826
|
+
? (output?.assets?.length ?? 0) > 1
|
|
827
|
+
: output?.assets?.some((a) => a.kind === "file");
|
|
828
|
+
if (!has) {
|
|
829
|
+
formats = formats.filter((f) => f !== "zip");
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return formats;
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* What the page's fields start with: an earlier step's result where they name one, and for a
|
|
836
|
+
* choice whose default leads to a step without a model here, the first value that does not.
|
|
837
|
+
*/
|
|
838
|
+
function prefillOf(step, run, closed) {
|
|
839
|
+
const scope = { def: run.definition, state: run.state, brand: {} };
|
|
840
|
+
const out = {};
|
|
841
|
+
for (const field of step.fields) {
|
|
842
|
+
if (!field.prefill) {
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
const value = resolveRef(dataRef(field.prefill), scope);
|
|
846
|
+
if (value !== undefined && value !== null && value !== "") {
|
|
847
|
+
out[field.id] = value;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
for (const [id, choice] of Object.entries(closed)) {
|
|
851
|
+
const field = step.fields.find((f) => f.id === id);
|
|
852
|
+
const start = out[id] ?? field?.default ?? (field?.kind === "toggle" ? false : undefined);
|
|
853
|
+
if (field && start !== undefined && choice.values.some((v) => String(v) === String(start))) {
|
|
854
|
+
out[id] = openValue(step, id, choice);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return out;
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Whether a run may hold a live conversation: a speech model is set up, and the wizard has live
|
|
861
|
+
* voice or video switched on. Its owner's test run always may.
|
|
862
|
+
*/
|
|
863
|
+
export async function talkAllowed(run) {
|
|
864
|
+
if (!(await conversationAvailable())) {
|
|
865
|
+
return false;
|
|
866
|
+
}
|
|
867
|
+
if (run.mode === "test") {
|
|
868
|
+
return true;
|
|
869
|
+
}
|
|
870
|
+
const w = await db.query.wizard.findFirst({ where: eq(schema.wizard.id, run.wizardId) });
|
|
871
|
+
const { enabled } = w?.runners ?? DEFAULT_RUNNER_SETTINGS;
|
|
872
|
+
return enabled.some((id) => CONVERSATION_RUNNERS.includes(id));
|
|
873
|
+
}
|
|
874
|
+
export async function runView(run, brand) {
|
|
875
|
+
// In the viewer's language every text of the view is the translation; ids and values stay.
|
|
876
|
+
const memory = run.lang ? await storedTranslation(run.wizardId, run.lang) : null;
|
|
877
|
+
const translated = memory ? translateDefinition(run.definition, memory) : null;
|
|
878
|
+
const def = translated?.def ?? run.definition;
|
|
879
|
+
const step = stepOf(def, run.cursor) ?? null;
|
|
880
|
+
const closed = step?.type === "page" && run.status === "waiting_input"
|
|
881
|
+
? await closedChoices(def, step, run.state.values)
|
|
882
|
+
: {};
|
|
883
|
+
const index = step ? def.steps.indexOf(step) : def.steps.length;
|
|
884
|
+
const refs = step?.type === "review"
|
|
885
|
+
? step.show
|
|
886
|
+
: step?.type === "result"
|
|
887
|
+
? [...new Set(step.deliverables.map((d) => d.from))]
|
|
888
|
+
: [];
|
|
889
|
+
const shownIds = refs.filter((ref) => !listRef(ref));
|
|
890
|
+
const listIds = [
|
|
891
|
+
...refs.map(listRef),
|
|
892
|
+
...(step?.type === "page" ? step.fields.map((f) => (f.kind === "list" ? f.list : null)) : []),
|
|
893
|
+
].filter((id) => Boolean(id));
|
|
894
|
+
const stored = listIds.length ? await storedLists(run) : {};
|
|
895
|
+
const lists = [...new Set(listIds)]
|
|
896
|
+
.filter((id) => stored[id])
|
|
897
|
+
.map((id) => {
|
|
898
|
+
const deliverable = step?.type === "result" ? step.deliverables.find((d) => listRef(d.from) === id) : undefined;
|
|
899
|
+
return {
|
|
900
|
+
...stored[id],
|
|
901
|
+
formats: deliverable
|
|
902
|
+
? deliverable.formats.filter((f) => LIST_FORMATS.includes(f) && (f !== "zip" || stored[id].def.check?.file))
|
|
903
|
+
: [],
|
|
904
|
+
label: deliverable?.label ?? null,
|
|
905
|
+
};
|
|
906
|
+
});
|
|
907
|
+
const shown = await Promise.all(shownIds
|
|
908
|
+
.map((id) => stepOf(def, id))
|
|
909
|
+
.filter((s) => Boolean(s))
|
|
910
|
+
.map(async (s) => {
|
|
911
|
+
const deliverable = step?.type === "result" ? step.deliverables.find((d) => d.from === s.id) : undefined;
|
|
912
|
+
return {
|
|
913
|
+
step: s,
|
|
914
|
+
output: run.state.outputs[s.id] ?? null,
|
|
915
|
+
formats: await availableFormats(s, run, deliverable?.formats),
|
|
916
|
+
label: deliverable?.label ?? null,
|
|
917
|
+
};
|
|
918
|
+
}));
|
|
919
|
+
return {
|
|
920
|
+
id: run.id,
|
|
921
|
+
status: run.status,
|
|
922
|
+
mode: run.mode,
|
|
923
|
+
wizard: {
|
|
924
|
+
title: def.title,
|
|
925
|
+
description: def.description,
|
|
926
|
+
avatar: def.avatar,
|
|
927
|
+
intro: def.intro,
|
|
928
|
+
},
|
|
929
|
+
lang: wizardLang(run.definition),
|
|
930
|
+
translation: run.lang && memory && translated
|
|
931
|
+
? {
|
|
932
|
+
lang: run.lang,
|
|
933
|
+
labels: translated.labels,
|
|
934
|
+
machine: machineTranslated(memory, run.definition),
|
|
935
|
+
results: run.state.translated?.[run.lang] ?? {},
|
|
936
|
+
}
|
|
937
|
+
: null,
|
|
938
|
+
talk: await talkAllowed(run),
|
|
939
|
+
step,
|
|
940
|
+
answered: run.state.history.map((id) => stepOf(def, id)).filter((s) => Boolean(s)),
|
|
941
|
+
values: run.state.values,
|
|
942
|
+
prefill: step?.type === "page" ? prefillOf(step, run, closed) : {},
|
|
943
|
+
...(step?.type === "page" ? viewContext(await pageContext(run, step)) : {}),
|
|
944
|
+
...(run.mode === "test" && run.state.decisions
|
|
945
|
+
? {
|
|
946
|
+
decisions: Object.fromEntries(Object.entries(run.state.decisions).map(([id, d]) => [
|
|
947
|
+
id,
|
|
948
|
+
{ ...d, over: def.steps.find((s) => s.id === id)?.next ?? [] },
|
|
949
|
+
])),
|
|
950
|
+
}
|
|
951
|
+
: {}),
|
|
952
|
+
closed,
|
|
953
|
+
outputs: Object.fromEntries(shownIds.map((id) => [id, run.state.outputs[id]]).filter(([, o]) => o)),
|
|
954
|
+
shown,
|
|
955
|
+
progress: { done: run.status === "done" ? def.steps.length : index, total: def.steps.length },
|
|
956
|
+
canBack: run.state.history.length > 0 && run.status !== "running",
|
|
957
|
+
error: run.error,
|
|
958
|
+
// A step's own words as it starts ("working") are the wizard's: in its translation too.
|
|
959
|
+
events: (await recentEvents(run.id, 0, 30)).map((ev) => memory?.[ev.message] && !ev.note ? { ...ev, message: memory[ev.message].text } : ev),
|
|
960
|
+
brand,
|
|
961
|
+
ask: run.status === "running" ? run.ask : null,
|
|
962
|
+
lists,
|
|
963
|
+
connections: step?.type === "page" && step.fields.some((f) => f.kind === "connection")
|
|
964
|
+
? await connectionViews(def.connections ?? [], scopeOf(run), await projectIdOf(run), textLang(wizardLang(def)))
|
|
965
|
+
: [],
|
|
966
|
+
keeps: Boolean(def.lists?.length || def.connections?.length),
|
|
967
|
+
shareUrl: run.shareToken ? `${env.publicUrl}/s/${run.shareToken}` : null,
|
|
968
|
+
expiresAt: run.expiresAt?.toISOString() ?? null,
|
|
969
|
+
};
|
|
970
|
+
}
|