synabun 2.0.0 → 2.0.7
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/CHANGELOG.md +97 -0
- package/NOTICE +5 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +59 -0
- package/benchmarks/fixtures/browser/README.md +1 -1
- package/benchmarks/fixtures/desktop/README.md +1 -1
- package/docs/whatsapp.md +153 -18
- package/hooks/claude-code/pre-compact.mjs +11 -0
- package/hooks/claude-code/prompt-submit.mjs +60 -184
- package/hooks/claude-code/session-start.mjs +73 -16
- package/hooks/claude-code/shared.mjs +26 -3
- package/hooks/claude-code/stop.mjs +5 -5
- package/lib/child-path.js +303 -0
- package/lib/claude-hooks.js +2 -1
- package/lib/data-home-migration.js +2 -0
- package/lib/desktop-app.js +265 -0
- package/lib/external-tools.js +7 -3
- package/lib/log-redact.js +102 -0
- package/lib/packaged-runtime.js +138 -11
- package/lib/start-launcher.js +88 -1
- package/mcp-server/dist/http.d.ts +3 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +15 -4
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +9 -6
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/budget-judgments.js +153 -0
- package/mcp-server/dist/services/budget-judgments.js.map +1 -0
- package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +41 -4
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +11 -0
- package/mcp-server/dist/services/identity.d.ts.map +1 -1
- package/mcp-server/dist/services/identity.js +24 -0
- package/mcp-server/dist/services/identity.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +13 -0
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +34 -2
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +4 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +10 -7
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +34 -13
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
- package/mcp-server/dist/tui.js +2 -1
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/dist/version.d.ts +5 -0
- package/mcp-server/dist/version.d.ts.map +1 -0
- package/mcp-server/dist/version.js +21 -0
- package/mcp-server/dist/version.js.map +1 -0
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -3
- package/mcp-server/run.mjs +15 -9
- package/mcp-server/src/http.ts +18 -3
- package/mcp-server/src/index.ts +9 -6
- package/mcp-server/src/services/budget-judgments.ts +183 -0
- package/mcp-server/src/services/codex-config-heal.ts +35 -4
- package/mcp-server/src/services/identity.ts +27 -0
- package/mcp-server/src/services/neural-interface.ts +34 -2
- package/mcp-server/src/services/typesafe-config.ts +4 -1
- package/mcp-server/src/tools/agents.ts +33 -14
- package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
- package/mcp-server/src/tui.ts +2 -1
- package/mcp-server/src/version.ts +22 -0
- package/neural-interface/i18n/en.json +2126 -209
- package/neural-interface/i18n/pt-BR.json +2123 -207
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/assistant-api.js +316 -57
- package/neural-interface/lib/assistant-brains/claude.js +19 -2
- package/neural-interface/lib/assistant-brains/codex.js +3 -0
- package/neural-interface/lib/assistant-brains/opencode.js +17 -4
- package/neural-interface/lib/assistant-budget-planner.js +390 -0
- package/neural-interface/lib/assistant-budget-split.js +408 -0
- package/neural-interface/lib/assistant-budget.js +94 -95
- package/neural-interface/lib/assistant-catalog.js +179 -27
- package/neural-interface/lib/assistant-config.js +91 -14
- package/neural-interface/lib/assistant-dispatch.js +494 -138
- package/neural-interface/lib/assistant-hidden-models.js +393 -66
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-memory.js +24 -10
- package/neural-interface/lib/assistant-persona.js +73 -12
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-pricing.js +21 -6
- package/neural-interface/lib/assistant-route-accounts.js +14 -0
- package/neural-interface/lib/assistant-route-gate.js +19 -3
- package/neural-interface/lib/assistant-router.js +373 -42
- package/neural-interface/lib/assistant-runtime.js +488 -59
- package/neural-interface/lib/assistant-task-prompt.js +13 -3
- package/neural-interface/lib/assistant-test-density.js +142 -0
- package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
- package/neural-interface/lib/assistant-user-context.js +908 -0
- package/neural-interface/lib/assistant-user-knowledge.js +328 -0
- package/neural-interface/lib/assistant-user-learn.js +404 -0
- package/neural-interface/lib/assistant-user-threads.js +226 -0
- package/neural-interface/lib/assistant-user-writer.js +183 -0
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/browser-background-tab.js +134 -0
- package/neural-interface/lib/browser-screenshot.js +678 -61
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +123 -9
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +33 -5
- package/neural-interface/lib/claude-panel-auth.js +221 -0
- package/neural-interface/lib/claude-profile-api-key.js +62 -0
- package/neural-interface/lib/claude-temporary.js +46 -7
- package/neural-interface/lib/cli-install-api.js +137 -0
- package/neural-interface/lib/cli-install.js +797 -0
- package/neural-interface/lib/cli-path.js +130 -0
- package/neural-interface/lib/cli-resolver.js +744 -0
- package/neural-interface/lib/cli-update-planner.js +143 -7
- package/neural-interface/lib/cloudflared/install.js +533 -0
- package/neural-interface/lib/cloudflared/manager.js +545 -0
- package/neural-interface/lib/cloudflared/pins.js +115 -0
- package/neural-interface/lib/cloudflared/pins.json +63 -0
- package/neural-interface/lib/cloudflared/tunnel.js +265 -0
- package/neural-interface/lib/codex-account-auth.js +178 -0
- package/neural-interface/lib/codex-accounts.js +489 -0
- package/neural-interface/lib/codex-capabilities.js +2 -1
- package/neural-interface/lib/codex-compat-rpc.js +8 -2
- package/neural-interface/lib/codex-mcp-approvals.js +5 -1
- package/neural-interface/lib/codex-panel-auth.js +62 -0
- package/neural-interface/lib/codex-session-settings.js +1 -1
- package/neural-interface/lib/codex-settings-schema.js +120 -0
- package/neural-interface/lib/desktop/config.js +3 -0
- package/neural-interface/lib/editor-completion-endpoint.js +264 -0
- package/neural-interface/lib/editor-completion-opencode.js +436 -0
- package/neural-interface/lib/editor-completion.js +714 -0
- package/neural-interface/lib/editor-config.js +149 -0
- package/neural-interface/lib/editor-files-api.js +268 -0
- package/neural-interface/lib/editor-git-api.js +134 -0
- package/neural-interface/lib/file-explorer-api.js +1039 -0
- package/neural-interface/lib/file-explorer-system.js +355 -0
- package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
- package/neural-interface/lib/file-explorer-watch.js +239 -0
- package/neural-interface/lib/git-bash/bzip2.js +339 -0
- package/neural-interface/lib/git-bash/claude-settings.js +73 -0
- package/neural-interface/lib/git-bash/extract-worker.js +34 -0
- package/neural-interface/lib/git-bash/install.js +529 -0
- package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
- package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
- package/neural-interface/lib/git-bash/manager.js +268 -0
- package/neural-interface/lib/git-bash/pins.js +206 -0
- package/neural-interface/lib/git-bash/pins.json +34 -0
- package/neural-interface/lib/git-bash/resolver.js +194 -0
- package/neural-interface/lib/git-diff-api.js +103 -0
- package/neural-interface/lib/git-read.js +234 -0
- package/neural-interface/lib/greeting/api.js +89 -0
- package/neural-interface/lib/greeting/budgets.js +90 -0
- package/neural-interface/lib/greeting/engine.js +461 -0
- package/neural-interface/lib/greeting/facts.js +320 -0
- package/neural-interface/lib/greeting/sources.js +252 -0
- package/neural-interface/lib/greeting/writer.js +139 -0
- package/neural-interface/lib/http-guards.js +189 -13
- package/neural-interface/lib/install-details.js +145 -0
- package/neural-interface/lib/machine-names.js +88 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/memory-api.js +31 -7
- package/neural-interface/lib/native-loop-providers.js +26 -8
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/plugin-installer.js +4 -2
- package/neural-interface/lib/project-storage.js +5 -7
- package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
- package/neural-interface/lib/pty-host/core.js +2 -1
- package/neural-interface/lib/pty-host/manager.js +27 -2
- package/neural-interface/lib/remote-policy.js +20 -11
- package/neural-interface/lib/request-gate.js +589 -0
- package/neural-interface/lib/session-title-generator.js +4 -3
- package/neural-interface/lib/system-about.js +340 -0
- package/neural-interface/lib/system-activity.js +66 -0
- package/neural-interface/lib/terminal-env.js +11 -3
- package/neural-interface/lib/terminal-path.js +55 -0
- package/neural-interface/lib/ui-blob-api.js +108 -0
- package/neural-interface/lib/ui-blob-store.js +68 -0
- package/neural-interface/lib/virtual-socket.js +12 -1
- package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
- package/neural-interface/lib/whatsapp/bridge.js +78 -34
- package/neural-interface/lib/whatsapp/commands.js +13 -14
- package/neural-interface/lib/whatsapp/config.js +134 -20
- package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
- package/neural-interface/lib/whatsapp/host-core.js +168 -35
- package/neural-interface/lib/whatsapp/inbound.js +5 -3
- package/neural-interface/lib/whatsapp/local-file.js +524 -0
- package/neural-interface/lib/whatsapp/manager.js +92 -32
- package/neural-interface/lib/whatsapp/paths.js +11 -1
- package/neural-interface/lib/whatsapp/protocol.js +65 -5
- package/neural-interface/lib/whatsapp/redact.js +63 -0
- package/neural-interface/lib/whatsapp/service.js +343 -91
- package/neural-interface/lib/whatsapp/transcription.js +463 -0
- package/neural-interface/package-lock.json +701 -2
- package/neural-interface/package.json +13 -2
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/install-app.html +9 -6
- package/neural-interface/public/offline.html +19 -3
- package/neural-interface/public/onboarding.html +107 -2
- package/neural-interface/public/shared/account-i18n.js +7 -0
- package/neural-interface/public/shared/account-menu.js +539 -0
- package/neural-interface/public/shared/account-usage.js +19 -0
- package/neural-interface/public/shared/account-view.js +335 -0
- package/neural-interface/public/shared/agent-runtime-options.js +50 -14
- package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
- package/neural-interface/public/shared/assistant/asst-api.js +32 -2
- package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
- package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
- package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
- package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
- package/neural-interface/public/shared/assistant/asst-render.js +197 -23
- package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
- package/neural-interface/public/shared/assistant/asst-route.js +95 -343
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +186 -2
- package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
- package/neural-interface/public/shared/blob-store.js +111 -22
- package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
- package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
- package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +80 -118
- package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
- package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
- package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
- package/neural-interface/public/shared/claude-account-view.js +3 -0
- package/neural-interface/public/shared/cli-install-view.js +392 -0
- package/neural-interface/public/shared/cli-status.js +282 -22
- package/neural-interface/public/shared/constants.js +6 -1
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +61 -0
- package/neural-interface/public/shared/desktop-bridge.js +39 -0
- package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
- package/neural-interface/public/shared/desktop-titlebar.js +153 -0
- package/neural-interface/public/shared/desktop-update-view.js +462 -0
- package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
- package/neural-interface/public/shared/editor/editor-ai.js +619 -0
- package/neural-interface/public/shared/editor/editor-commands.js +212 -0
- package/neural-interface/public/shared/editor/editor-compare.js +103 -0
- package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
- package/neural-interface/public/shared/editor/editor-doc.js +257 -0
- package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
- package/neural-interface/public/shared/editor/editor-git.js +95 -0
- package/neural-interface/public/shared/editor/editor-languages.js +156 -0
- package/neural-interface/public/shared/editor/editor-lint.js +229 -0
- package/neural-interface/public/shared/editor/editor-loader.js +51 -0
- package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
- package/neural-interface/public/shared/editor/editor-menu.js +233 -0
- package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
- package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
- package/neural-interface/public/shared/editor/editor-palette.js +125 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
- package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
- package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
- package/neural-interface/public/shared/editor/editor-safety.js +500 -0
- package/neural-interface/public/shared/editor/editor-search.js +352 -0
- package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
- package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
- package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
- package/neural-interface/public/shared/editor/editor-theme.js +244 -0
- package/neural-interface/public/shared/editor/editor-view.js +332 -0
- package/neural-interface/public/shared/explorer/fx-api.js +216 -0
- package/neural-interface/public/shared/explorer/fx-links.js +204 -0
- package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
- package/neural-interface/public/shared/explorer/fx-main.js +865 -0
- package/neural-interface/public/shared/explorer/fx-names.js +87 -0
- package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
- package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
- package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
- package/neural-interface/public/shared/file-links.js +339 -0
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
- package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
- package/neural-interface/public/shared/html-shell.js +161 -165
- package/neural-interface/public/shared/i18n.js +19 -1
- package/neural-interface/public/shared/install-details-view.js +86 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +131 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +22 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
- package/neural-interface/public/shared/pwa-install-page.js +32 -2
- package/neural-interface/public/shared/pwa-install.css +3 -3
- package/neural-interface/public/shared/pwa-install.js +100 -1
- package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
- package/neural-interface/public/shared/settings/settings-about.js +254 -0
- package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
- package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
- package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
- package/neural-interface/public/shared/settings/settings-ia.js +33 -4
- package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
- package/neural-interface/public/shared/showcase-privacy.js +489 -0
- package/neural-interface/public/shared/start-bridge.js +238 -19
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +1710 -770
- package/neural-interface/public/shared/synabun-mascot.js +68 -58
- package/neural-interface/public/shared/tunnel-view.js +167 -0
- package/neural-interface/public/shared/ui-claude-panel.js +369 -229
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-detail.js +15 -0
- package/neural-interface/public/shared/ui-explorer.js +22 -0
- package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
- package/neural-interface/public/shared/ui-file-links.js +103 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-help.js +26 -0
- package/neural-interface/public/shared/ui-invite.js +126 -116
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-keybinds.js +4 -0
- package/neural-interface/public/shared/ui-loading.js +187 -19
- package/neural-interface/public/shared/ui-menubar.js +25 -33
- package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
- package/neural-interface/public/shared/ui-navbar.js +48 -22
- package/neural-interface/public/shared/ui-provider-auth.js +104 -0
- package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
- package/neural-interface/public/shared/ui-sessions.js +9 -3
- package/neural-interface/public/shared/ui-settings.js +509 -490
- package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +22 -0
- package/neural-interface/public/shared/ui-terminal.js +40 -5
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-tunnel.js +130 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
- package/neural-interface/public/shared/ui-update.js +594 -5
- package/neural-interface/public/shared/ui-whatsapp.js +21 -6
- package/neural-interface/public/shared/ui-whiteboard.js +12 -0
- package/neural-interface/public/shared/ui-worker-end.js +245 -0
- package/neural-interface/public/shared/ui-workspaces.js +41 -27
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +253 -0
- package/neural-interface/public/sw.js +2 -2
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
- package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
- package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
- package/neural-interface/scripts/build-vendor.mjs +78 -2
- package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
- package/neural-interface/server.js +2085 -1629
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/package.json +5 -8
- package/setup.js +53 -15
- package/benchmarks/browser-live-results.json +0 -79
- package/benchmarks/browser-v2-results.json +0 -258
- package/neural-interface/lib/codex-capabilities.d.ts +0 -41
- package/neural-interface/public/category-logos/claude.svg +0 -1
- package/neural-interface/public/category-logos/gemini-color.svg +0 -1
- package/neural-interface/public/category-logos/openai.svg +0 -1
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude.svg +0 -1
- package/neural-interface/public/gallery.svg +0 -6
- package/neural-interface/public/leonardoai.svg +0 -1
- package/neural-interface/public/mcp.svg +0 -4
- package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
- package/neural-interface/public/pin-svgrepo-com.svg +0 -25
- package/neural-interface/public/sessionicon.svg +0 -8
- package/neural-interface/public/toolbar-album.svg +0 -8
- package/neural-interface/public/toolbar-question.svg +0 -6
- package/neural-interface/public/toolbar-sessions.svg +0 -6
- package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const cache = new Map();
|
|
7
|
+
const featureCache = new Map();
|
|
8
|
+
export const CODEX_SETTINGS_KEYS = Object.freeze([
|
|
9
|
+
'model', 'review_model', 'model_provider', 'approval_policy', 'sandbox_mode', 'personality',
|
|
10
|
+
'service_tier', 'model_reasoning_effort', 'model_reasoning_summary', 'model_verbosity',
|
|
11
|
+
'model_context_window', 'model_auto_compact_token_limit', 'web_search',
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
export function schemaEnum(schema, definitions) {
|
|
15
|
+
if (!schema) return [];
|
|
16
|
+
if (schema.$ref) return schemaEnum(definitions[schema.$ref.split('/').pop()], definitions);
|
|
17
|
+
return [...new Set([...(schema.enum || []).filter(v => typeof v === 'string'),
|
|
18
|
+
...['anyOf', 'oneOf', 'allOf'].flatMap(k => (schema[k] || []).flatMap(s => schemaEnum(s, definitions)))])];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function settingsSchemaFromProtocol(configSchema, threadSchema) {
|
|
22
|
+
const properties = configSchema.definitions?.Config?.properties || {};
|
|
23
|
+
const fields = {};
|
|
24
|
+
for (const key of CODEX_SETTINGS_KEYS) {
|
|
25
|
+
const field = properties[key];
|
|
26
|
+
if (!field || /@deprecated/.test(field.description || '')) continue;
|
|
27
|
+
const values = schemaEnum(field, configSchema.definitions || {});
|
|
28
|
+
fields[key] = { values, enumerated: values.length > 0,
|
|
29
|
+
type: key.includes('token_limit') || key === 'model_context_window' ? 'number' : 'select' };
|
|
30
|
+
}
|
|
31
|
+
// Some older runtimes expose personality only on thread/turn params. Newer
|
|
32
|
+
// ones explicitly retain it as a no-op: never present a no-op preference.
|
|
33
|
+
const personality = threadSchema.properties?.personality;
|
|
34
|
+
if (personality && !/@deprecated/.test(personality.description || '')) {
|
|
35
|
+
fields.personality = { type: 'select', values: schemaEnum(personality, threadSchema.definitions || {}) };
|
|
36
|
+
}
|
|
37
|
+
return { fields };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Generate from the executable we actually launch; never use the global home. */
|
|
41
|
+
export function readCodexSettingsSchema(codexBin) {
|
|
42
|
+
const stamp = statSync(codexBin);
|
|
43
|
+
const key = `${codexBin}:${stamp.mtimeMs}:${stamp.size}`;
|
|
44
|
+
if (cache.has(key)) return cache.get(key);
|
|
45
|
+
const home = mkdtempSync(join(tmpdir(), 'synabun-codex-schema-'));
|
|
46
|
+
try {
|
|
47
|
+
const out = join(home, 'schema');
|
|
48
|
+
const run = spawnSync(codexBin, ['app-server', 'generate-json-schema', '--experimental', '--out', out], {
|
|
49
|
+
env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 15000, windowsHide: true,
|
|
50
|
+
});
|
|
51
|
+
if (run.error || run.status !== 0) throw new Error('Codex could not report its settings schema');
|
|
52
|
+
const read = name => JSON.parse(readFileSync(join(out, 'v2', `${name}.json`), 'utf8'));
|
|
53
|
+
const schema = settingsSchemaFromProtocol(read('ConfigReadResponse'), read('ThreadStartParams'));
|
|
54
|
+
// Protocol enums can retain deprecated variants that the config loader
|
|
55
|
+
// already rejects (0.162 still exports `untrusted`). Ask the loader too.
|
|
56
|
+
// `features list` validates config locally and never runs a model or tool.
|
|
57
|
+
for (const [fieldKey, field] of Object.entries(schema.fields)) {
|
|
58
|
+
field.values = field.values.filter(value => {
|
|
59
|
+
const check = spawnSync(codexBin, ['-c', `${fieldKey}=${JSON.stringify(value)}`, 'features', 'list'], {
|
|
60
|
+
env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 5000, windowsHide: true,
|
|
61
|
+
});
|
|
62
|
+
return !check.error && check.status === 0;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
cache.set(key, schema);
|
|
66
|
+
return schema;
|
|
67
|
+
} finally { rmSync(home, { recursive: true, force: true }); }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function validateCodexSettings(config, schema, features = []) {
|
|
71
|
+
const result = {};
|
|
72
|
+
for (const [key, value] of Object.entries(config || {})) {
|
|
73
|
+
if (key === 'features') {
|
|
74
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid feature settings');
|
|
75
|
+
Object.assign(result, validateCodexSettings(Object.fromEntries(Object.entries(value).map(([k, v]) => [`features.${k}`, v])), schema, features));
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (key.startsWith('features.')) {
|
|
79
|
+
const feature = features.find(f => `features.${f.name}` === key && ['stable', 'beta'].includes(f.stage) && f.configurable !== false);
|
|
80
|
+
if (!feature || typeof value !== 'boolean') throw new Error(`Unsupported Codex feature: ${key}`);
|
|
81
|
+
} else {
|
|
82
|
+
const field = schema.fields?.[key];
|
|
83
|
+
if (!field) throw new Error(`Unsupported Codex setting: ${key}`);
|
|
84
|
+
if (value !== null) {
|
|
85
|
+
if (field.type === 'number') {
|
|
86
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`Invalid token count: ${key}`);
|
|
87
|
+
} else if (typeof value !== 'string' || !value || ((field.enumerated || field.values.length) && !field.values.includes(value))) {
|
|
88
|
+
// Preserve granular approval policies; only the UI's string choices are editable.
|
|
89
|
+
throw new Error(`Invalid Codex setting value: ${key}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
result[key] = value;
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Some stable flags are now mandatory and ignore false (e.g. unified_exec).
|
|
99
|
+
* Check both states with the local loader instead of offering a no-op switch. */
|
|
100
|
+
export function readCodexFeatureSupport(codexBin, features) {
|
|
101
|
+
const names = features.filter(f => ['stable', 'beta'].includes(f.stage) && /^[A-Za-z0-9_-]+$/.test(f.name)).map(f => f.name).sort();
|
|
102
|
+
const stat = statSync(codexBin);
|
|
103
|
+
const key = `${codexBin}:${stat.mtimeMs}:${stat.size}:${names.join(',')}`;
|
|
104
|
+
if (featureCache.has(key)) return featureCache.get(key);
|
|
105
|
+
const home = mkdtempSync(join(tmpdir(), 'synabun-feature-support-'));
|
|
106
|
+
try {
|
|
107
|
+
const states = [false, true].map(value => {
|
|
108
|
+
const check = spawnSync(codexBin, ['-c', `features={${names.map(name => `${name}=${value}`).join(',')}}`, 'features', 'list'], {
|
|
109
|
+
env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 10000, windowsHide: true,
|
|
110
|
+
});
|
|
111
|
+
if (check.error || check.status !== 0) throw new Error('Codex could not validate feature switches');
|
|
112
|
+
return new Map(check.stdout.split('\n').map(line => {
|
|
113
|
+
const parts = line.trim().split(/\s+/); return [parts[0], parts.at(-1)];
|
|
114
|
+
}));
|
|
115
|
+
});
|
|
116
|
+
const supported = Object.fromEntries(names.map(name => [name, states[0].get(name) === 'false' && states[1].get(name) === 'true']));
|
|
117
|
+
featureCache.set(key, supported);
|
|
118
|
+
return supported;
|
|
119
|
+
} finally { rmSync(home, { recursive: true, force: true }); }
|
|
120
|
+
}
|
|
@@ -446,6 +446,9 @@ export const DESKTOP_DEFAULTS = Object.freeze({
|
|
|
446
446
|
bundleIds: ['com.apple.Terminal', 'com.googlecode.iterm2', 'dev.warp.Warp-Stable', 'com.mitchellh.ghostty', 'net.kovidgoyal.kitty', 'com.github.wez.wezterm', 'org.alacritty', 'co.zeit.hyper'],
|
|
447
447
|
reason: 'Terminal emulators are off-limits (your other agents run there); use your own shell tool instead',
|
|
448
448
|
},
|
|
449
|
+
// The SynaBun desktop app (packaging/desktop): every window it has is SynaBun's own interface, as the
|
|
450
|
+
// synabun-ui rule below says of SynaBun in a browser or a web app.
|
|
451
|
+
{ id: 'synabun-app', bundleIds: ['ai.synabun.app'], reason: "SynaBun's own interface" },
|
|
449
452
|
],
|
|
450
453
|
// Arrays replace wholesale (mergeConfig): a user's own protectedWindows (or
|
|
451
454
|
// blockedApps) list drops every default rule below, including the localized
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// ── AI autocomplete through an endpoint of the user's own ──
|
|
2
|
+
// A local Ollama, or anything that speaks the OpenAI completions or chat
|
|
3
|
+
// protocol. What is sent (docs/design/code-editor/SPEC.md, section 9.6):
|
|
4
|
+
//
|
|
5
|
+
// ollama + fim POST {url}/api/generate { model, prompt: prefix, suffix, stream, options, keep_alive }
|
|
6
|
+
// ollama + chat POST {url}/api/chat { model, messages, stream, options, keep_alive }
|
|
7
|
+
// openai + fim POST {url} { model, prompt: prefix, suffix, max_tokens, temperature, stream }
|
|
8
|
+
// openai + chat POST {url} { model, messages, max_tokens, temperature, stream }
|
|
9
|
+
//
|
|
10
|
+
// `Authorization: Bearer` is added when SYNABUN_EDITOR_AI_KEY is set; the key is
|
|
11
|
+
// never part of an error, in any form: nothing an endpoint says about a failure
|
|
12
|
+
// (an error body, an error inside a stream) is passed on. An endpoint may repeat
|
|
13
|
+
// the request's Authorization header as sent, URL-encoded, JSON-escaped or in a
|
|
14
|
+
// form nobody listed, so its words are only read to pick one of a few sentences
|
|
15
|
+
// of this file's own (endpointFailure). Every failure of a call leaves here as
|
|
16
|
+
// an EndpointFailure with such a sentence. The answer is read as it streams
|
|
17
|
+
// (server-sent events, one JSON object per line, or one JSON body) until it
|
|
18
|
+
// ends, until the caller says it holds enough, or until the request is aborted.
|
|
19
|
+
|
|
20
|
+
const MAX_TOKENS = 256;
|
|
21
|
+
const TEMPERATURE = 0.1;
|
|
22
|
+
const MAX_REPLY_CHARS = 16_000;
|
|
23
|
+
|
|
24
|
+
const trimSlashes = (url) => String(url || '').replace(/\/+$/, '');
|
|
25
|
+
|
|
26
|
+
const REDACTED = '[redacted]';
|
|
27
|
+
// What follows "Bearer" in a header an endpoint echoes: the characters a token is made of (lib/editor-config.js).
|
|
28
|
+
const BEARER_TOKEN = /\b(Bearer)\s+[A-Za-z0-9._~+/=:@-]+/gi;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A text without the endpoint's key as it was sent: the key itself wherever it stands, and whatever follows
|
|
32
|
+
* "Bearer" (a credential whoever it belongs to). It knows the key in one form only, so it is not what keeps the
|
|
33
|
+
* key out of an endpoint's errors (nothing an endpoint says is passed on: endpointFailure); it is a second rule
|
|
34
|
+
* for text of this server's own.
|
|
35
|
+
* @param {string} text
|
|
36
|
+
* @param {string} [key] the endpoint's key (SYNABUN_EDITOR_AI_KEY), when one is set
|
|
37
|
+
*/
|
|
38
|
+
export function redactKey(text, key = '') {
|
|
39
|
+
let out = String(text ?? '');
|
|
40
|
+
if (key) out = out.split(String(key)).join(REDACTED);
|
|
41
|
+
return out.replace(BEARER_TOKEN, `$1 ${REDACTED}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** A failure of an endpoint call, in words of this file only: safe for the page, a test's row and the log. */
|
|
45
|
+
export class EndpointFailure extends Error {
|
|
46
|
+
/** @param {string} message @param {{ status?: number | null, kind?: string | null }} [o] */
|
|
47
|
+
constructor(message, { status = null, kind = null } = {}) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.name = 'EndpointFailure';
|
|
50
|
+
this.status = status;
|
|
51
|
+
this.kind = kind;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// What a failure is called, by what the answer mentions and by its status. The first sentence whose words are
|
|
56
|
+
// found wins; with no words found, the first whose statuses hold this one.
|
|
57
|
+
const FAILURE_KINDS = [
|
|
58
|
+
{ kind: 'model', says: 'it does not have that model', statuses: [], words: /model.{0,60}(?:not found|does not exist|doesn't exist|unknown|not supported|not available)|(?:no such|unknown|invalid) model|try pulling/i },
|
|
59
|
+
{ kind: 'key', says: 'it refused the key', statuses: [401, 403], words: /unauthori[sz]ed|incorrect api key|invalid.{0,12}(?:api.?key|token|credential)|authentication|not authenticated|forbidden|permission denied/i },
|
|
60
|
+
{ kind: 'rate', says: 'it takes no more requests for now', statuses: [429], words: /rate.?limit|too many requests|quota|insufficient.{0,12}(?:credit|balance|funds|quota)/i },
|
|
61
|
+
{ kind: 'long', says: 'the request is too long for the model', statuses: [413], words: /context.{0,20}(?:length|window)|too long|maximum.{0,20}tokens|too many tokens/i },
|
|
62
|
+
{ kind: 'busy', says: 'it is busy or still loading the model', statuses: [502, 503, 504], words: /overloaded|unavailable|loading model|still loading|timed? ?out/i },
|
|
63
|
+
{ kind: 'address', says: 'nothing takes completions at that address', statuses: [404, 405], words: null },
|
|
64
|
+
];
|
|
65
|
+
// Why a request never got an answer, by the code of what ended it (fetch's `cause`).
|
|
66
|
+
const NETWORK_KINDS = {
|
|
67
|
+
ECONNREFUSED: 'nothing is listening at its address',
|
|
68
|
+
ENOTFOUND: 'its address is not a name that is known',
|
|
69
|
+
EAI_AGAIN: 'its address could not be looked up',
|
|
70
|
+
ECONNRESET: 'it closed the connection',
|
|
71
|
+
ETIMEDOUT: 'it did not answer in time',
|
|
72
|
+
UND_ERR_CONNECT_TIMEOUT: 'it did not answer in time',
|
|
73
|
+
UND_ERR_SOCKET: 'it closed the connection',
|
|
74
|
+
CERT_HAS_EXPIRED: 'its certificate is not accepted',
|
|
75
|
+
DEPTH_ZERO_SELF_SIGNED_CERT: 'its certificate is not accepted',
|
|
76
|
+
SELF_SIGNED_CERT_IN_CHAIN: 'its certificate is not accepted',
|
|
77
|
+
UNABLE_TO_VERIFY_LEAF_SIGNATURE: 'its certificate is not accepted',
|
|
78
|
+
ERR_TLS_CERT_ALTNAME_INVALID: 'its certificate is not accepted',
|
|
79
|
+
ERR_INVALID_URL: 'its address is not a URL',
|
|
80
|
+
};
|
|
81
|
+
const READ_FOR_KIND = 4000;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* What an endpoint's failure is called. `said` (its error body, or the error inside its stream) is read for what
|
|
85
|
+
* it mentions and for nothing else: not a character of it is in the result.
|
|
86
|
+
* @param {number | null} status the HTTP status, or null for an error an answer carried in its stream
|
|
87
|
+
* @param {string} [said]
|
|
88
|
+
* @returns {EndpointFailure}
|
|
89
|
+
*/
|
|
90
|
+
export function endpointFailure(status, said = '') {
|
|
91
|
+
const words = String(said ?? '').slice(0, READ_FOR_KIND);
|
|
92
|
+
const code = Number.isInteger(status) ? status : null;
|
|
93
|
+
const found = FAILURE_KINDS.find((each) => each.words && each.words.test(words))
|
|
94
|
+
|| (code === null ? null : FAILURE_KINDS.find((each) => each.statuses.includes(code)));
|
|
95
|
+
const lead = code === null ? 'The endpoint reported an error' : `The endpoint answered ${code}`;
|
|
96
|
+
return new EndpointFailure(found ? `${lead}: ${found.says}` : lead, { status: code, kind: found?.kind ?? null });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Whatever ended an endpoint call, as an EndpointFailure: its own when it is one; a request that got no answer by
|
|
101
|
+
* the code of what ended it; anything else without its words (they are not this file's).
|
|
102
|
+
*/
|
|
103
|
+
export function asEndpointFailure(error) {
|
|
104
|
+
if (error instanceof EndpointFailure) return error;
|
|
105
|
+
// fetch says "fetch failed" and keeps the reason in `cause` (several of them when a name has several addresses).
|
|
106
|
+
const causes = [error?.cause, error, error?.cause?.cause, ...(Array.isArray(error?.cause?.errors) ? error.cause.errors : [])];
|
|
107
|
+
const code = causes.map((each) => each?.code).find((each) => typeof each === 'string' && Object.hasOwn(NETWORK_KINDS, each));
|
|
108
|
+
return new EndpointFailure(code ? `The endpoint could not be reached: ${NETWORK_KINDS[code]}` : 'The endpoint could not be asked', { kind: code ? 'network' : null });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The HTTP request of one completion.
|
|
113
|
+
* @param {object} o
|
|
114
|
+
* @param {{ kind: 'ollama' | 'openai', url: string, model: string, shape: 'fim' | 'chat' }} o.endpoint `ai.endpoint`
|
|
115
|
+
* @param {string} [o.key] the bearer token, when one is set
|
|
116
|
+
* @param {{ prefix: string, suffix: string }} o.request
|
|
117
|
+
* @param {{ system: string, user: string }} o.prompt the chat messages (lib/editor-completion.js buildChatPrompt)
|
|
118
|
+
* @returns {{ url: string, headers: object, body: object }}
|
|
119
|
+
*/
|
|
120
|
+
export function endpointRequest({ endpoint, key = '', request, prompt }) {
|
|
121
|
+
const headers = { 'Content-Type': 'application/json', Accept: 'text/event-stream, application/x-ndjson, application/json' };
|
|
122
|
+
if (key) headers.Authorization = `Bearer ${key}`;
|
|
123
|
+
const messages = [{ role: 'system', content: prompt.system }, { role: 'user', content: prompt.user }];
|
|
124
|
+
if (endpoint.kind === 'ollama') {
|
|
125
|
+
// The address is the server's; a pasted /api/generate or /api/chat is not doubled.
|
|
126
|
+
const base = trimSlashes(endpoint.url).replace(/\/api\/(?:generate|chat)$/, '');
|
|
127
|
+
const common = { model: endpoint.model, stream: true, options: { temperature: TEMPERATURE, num_predict: MAX_TOKENS }, keep_alive: '30m' };
|
|
128
|
+
return endpoint.shape === 'chat'
|
|
129
|
+
? { url: `${base}/api/chat`, headers, body: { ...common, messages } }
|
|
130
|
+
: { url: `${base}/api/generate`, headers, body: { ...common, prompt: request.prefix, suffix: request.suffix } };
|
|
131
|
+
}
|
|
132
|
+
const common = { model: endpoint.model, max_tokens: MAX_TOKENS, temperature: TEMPERATURE, stream: true };
|
|
133
|
+
return endpoint.shape === 'chat'
|
|
134
|
+
? { url: endpoint.url, headers, body: { ...common, messages } }
|
|
135
|
+
: { url: endpoint.url, headers, body: { ...common, prompt: request.prefix, suffix: request.suffix } };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* What one object of an answer holds, whichever of the four protocols it is.
|
|
140
|
+
* @param {object} chunk
|
|
141
|
+
* @returns {{ text: string, done: boolean, tokens: { input: number, output: number } | null, error: string | null }}
|
|
142
|
+
* `error`: what the failure is called (endpointFailure), never the endpoint's own words
|
|
143
|
+
*/
|
|
144
|
+
export function readChunk(chunk) {
|
|
145
|
+
const out = { text: '', done: false, tokens: null, error: null };
|
|
146
|
+
if (!chunk || typeof chunk !== 'object') return out;
|
|
147
|
+
if (chunk.error) {
|
|
148
|
+
let said = '';
|
|
149
|
+
try { said = typeof chunk.error === 'string' ? chunk.error : JSON.stringify(chunk.error); } catch { said = ''; }
|
|
150
|
+
out.error = endpointFailure(null, said).message;
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : null;
|
|
154
|
+
const text = chunk.response ?? chunk.message?.content ?? choice?.text ?? choice?.delta?.content ?? choice?.message?.content;
|
|
155
|
+
if (typeof text === 'string') out.text = text;
|
|
156
|
+
out.done = chunk.done === true || (choice != null && choice.finish_reason != null);
|
|
157
|
+
const input = chunk.prompt_eval_count ?? chunk.usage?.prompt_tokens;
|
|
158
|
+
const output = chunk.eval_count ?? chunk.usage?.completion_tokens;
|
|
159
|
+
if (Number.isFinite(input) || Number.isFinite(output)) out.tokens = { input: Math.round(input || 0), output: Math.round(output || 0) };
|
|
160
|
+
return out;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Read an answer to its end, or until `isDone(text)` says it holds enough.
|
|
165
|
+
* @param {Response} res
|
|
166
|
+
* @param {{ isDone?: (text: string) => boolean }} [o]
|
|
167
|
+
* @returns {Promise<{ text: string, tokens: { input: number, output: number } | null, stoppedEarly: boolean }>}
|
|
168
|
+
* rejects with an EndpointFailure for an error the answer carries, and for an answer that cannot be read
|
|
169
|
+
*/
|
|
170
|
+
export async function readEndpointAnswer(res, { isDone = () => false } = {}) {
|
|
171
|
+
let text = '';
|
|
172
|
+
let tokens = null;
|
|
173
|
+
let pending = ''; // the line that is still arriving
|
|
174
|
+
let unparsed = ''; // lines that were not objects on their own: one JSON body spread over several lines
|
|
175
|
+
let finished = false;
|
|
176
|
+
|
|
177
|
+
const take = (chunk) => {
|
|
178
|
+
const part = readChunk(chunk);
|
|
179
|
+
if (part.error) throw new EndpointFailure(part.error, { kind: 'reported' });
|
|
180
|
+
text += part.text;
|
|
181
|
+
if (part.tokens) tokens = part.tokens;
|
|
182
|
+
if (part.done) finished = true;
|
|
183
|
+
};
|
|
184
|
+
const line = (raw) => {
|
|
185
|
+
let value = raw.trim();
|
|
186
|
+
if (!value || value.startsWith(':') || value.startsWith('event:') || value.startsWith('id:') || value.startsWith('retry:')) return;
|
|
187
|
+
if (value.startsWith('data:')) value = value.slice(5).trim();
|
|
188
|
+
if (value === '[DONE]') { finished = true; return; }
|
|
189
|
+
let chunk;
|
|
190
|
+
try { chunk = JSON.parse(value); } catch { unparsed += `${raw}\n`; return; }
|
|
191
|
+
take(chunk);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const reader = res.body?.getReader?.();
|
|
195
|
+
if (!reader) {
|
|
196
|
+
for (const each of (await res.text()).split('\n')) line(each);
|
|
197
|
+
} else {
|
|
198
|
+
const decoder = new TextDecoder();
|
|
199
|
+
let stoppedEarly = false;
|
|
200
|
+
try {
|
|
201
|
+
for (;;) {
|
|
202
|
+
const { value, done } = await reader.read();
|
|
203
|
+
if (done) break;
|
|
204
|
+
pending += decoder.decode(value, { stream: true });
|
|
205
|
+
const lines = pending.split('\n');
|
|
206
|
+
pending = lines.pop();
|
|
207
|
+
for (const each of lines) line(each);
|
|
208
|
+
if (finished) break;
|
|
209
|
+
if (text.length > MAX_REPLY_CHARS || isDone(text)) { stoppedEarly = true; break; }
|
|
210
|
+
}
|
|
211
|
+
} finally {
|
|
212
|
+
// Whatever is still on its way is not wanted: the connection is given back.
|
|
213
|
+
try { await reader.cancel(); } catch { /* already closed */ }
|
|
214
|
+
}
|
|
215
|
+
if (stoppedEarly) return { text, tokens, stoppedEarly: true };
|
|
216
|
+
if (pending) line(pending);
|
|
217
|
+
}
|
|
218
|
+
if (unparsed.trim() && !text) {
|
|
219
|
+
let whole;
|
|
220
|
+
try { whole = JSON.parse(unparsed); } catch { throw new EndpointFailure('The endpoint did not answer in a form this editor reads', { kind: 'form' }); }
|
|
221
|
+
take(whole);
|
|
222
|
+
}
|
|
223
|
+
return { text, tokens, stoppedEarly: false };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** @param {{ fetchImpl?: typeof fetch }} [deps] */
|
|
227
|
+
export function createEndpointCompletionEngine({ fetchImpl = (...args) => fetch(...args) } = {}) {
|
|
228
|
+
return {
|
|
229
|
+
/**
|
|
230
|
+
* One completion.
|
|
231
|
+
* @param {object} o
|
|
232
|
+
* @param {{ kind: string, url: string, model: string, shape: string }} o.endpoint
|
|
233
|
+
* @param {string} [o.key]
|
|
234
|
+
* @param {{ prefix: string, suffix: string }} o.request
|
|
235
|
+
* @param {{ system: string, user: string }} o.prompt
|
|
236
|
+
* @param {AbortSignal} [o.signal]
|
|
237
|
+
* @param {(text: string) => boolean} [o.isDone] the first stop condition: the answer so far holds all that would be shown
|
|
238
|
+
* @returns {Promise<{ text: string, tokens: { input: number, output: number } | null }>} rejects with an
|
|
239
|
+
* EndpointFailure (its message holds nothing the endpoint said), or with the abort when `signal` fired
|
|
240
|
+
*/
|
|
241
|
+
async complete({ endpoint, key = '', request, prompt, signal, isDone } = {}) {
|
|
242
|
+
const { url, headers, body } = endpointRequest({ endpoint, key, request, prompt });
|
|
243
|
+
// Our own controller, so an answer that holds enough can be cut off without the caller aborting.
|
|
244
|
+
const own = new AbortController();
|
|
245
|
+
const onAbort = () => own.abort();
|
|
246
|
+
if (signal?.aborted) own.abort();
|
|
247
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
248
|
+
try {
|
|
249
|
+
const res = await fetchImpl(url, { method: 'POST', headers, body: JSON.stringify(body), signal: own.signal });
|
|
250
|
+
// What it says is read for what the failure is called, and stays here.
|
|
251
|
+
if (!res.ok) throw endpointFailure(res.status, await res.text().catch(() => ''));
|
|
252
|
+
const answer = await readEndpointAnswer(res, { isDone });
|
|
253
|
+
return { text: answer.text, tokens: answer.tokens };
|
|
254
|
+
} catch (error) {
|
|
255
|
+
// The caller gave up: that is not the endpoint's failure.
|
|
256
|
+
if (signal?.aborted) throw error;
|
|
257
|
+
throw asEndpointFailure(error);
|
|
258
|
+
} finally {
|
|
259
|
+
signal?.removeEventListener('abort', onAbort);
|
|
260
|
+
own.abort();
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|