synabun 2.0.10 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +86 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +22 -0
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +2 -2
- package/hooks/claude-code/pre-websearch.mjs +12 -1
- package/hooks/claude-code/shared.mjs +5 -0
- package/hooks/claude-code/stop.mjs +1 -1
- package/lib/claude-hooks.js +172 -18
- package/lib/client-config-repair.js +17 -8
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +1 -1
- package/lib/first-launch-protection.js +57 -12
- package/lib/mcp-dist.js +176 -0
- package/lib/release-npm-ci.mjs +138 -0
- package/lib/release-publish.mjs +91 -0
- package/lib/release-scan.mjs +50 -0
- package/lib/server-crash-record.js +123 -0
- package/lib/server-exit.js +318 -0
- package/lib/server-supervisor.js +508 -0
- package/lib/start-launcher.js +37 -4
- package/lib/tools-cli.js +45 -0
- package/lib/update-snapshot.js +228 -0
- package/lib/virustotal.mjs +192 -0
- package/lib/windows-crash-event.js +269 -0
- package/mcp-server/dist/http.d.ts +20 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +59 -12
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts +33 -27
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +128 -143
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/preload.js +1 -1
- package/mcp-server/dist/preload.js.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +81 -28
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
- package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
- package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +16 -10
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +67 -11
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts +7 -2
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +20 -62
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
- package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-catalog.js +339 -0
- package/mcp-server/dist/services/tool-catalog.js.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
- package/mcp-server/dist/services/tool-list.d.ts +108 -0
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list.js +334 -0
- package/mcp-server/dist/services/tool-list.js.map +1 -0
- package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-runtime.js +400 -0
- package/mcp-server/dist/services/tool-runtime.js.map +1 -0
- package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
- package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
- package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
- package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +10 -10
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +22 -13
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill-tools.js +639 -0
- package/mcp-server/dist/tools/skill-tools.js.map +1 -0
- package/mcp-server/dist/tools/skill.d.ts +7 -0
- package/mcp-server/dist/tools/skill.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill.js +11 -0
- package/mcp-server/dist/tools/skill.js.map +1 -0
- package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
- package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
- package/mcp-server/dist/tools/tool-groups.js +147 -0
- package/mcp-server/dist/tools/tool-groups.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-format.js +7 -4
- package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/http.ts +75 -12
- package/mcp-server/src/index.ts +147 -154
- package/mcp-server/src/preload.ts +12 -12
- package/mcp-server/src/services/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/neural-interface.ts +79 -20
- package/mcp-server/src/services/server-instructions.ts +27 -63
- package/mcp-server/src/services/tool-catalog.ts +403 -0
- package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
- package/mcp-server/src/services/tool-list.ts +410 -0
- package/mcp-server/src/services/tool-runtime.ts +457 -0
- package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +22 -14
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/skill-tools.ts +589 -0
- package/mcp-server/src/tools/skill.ts +13 -0
- package/mcp-server/src/tools/tool-groups.ts +164 -0
- package/mcp-server/src/tools/whiteboard-format.ts +9 -3
- package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
- package/neural-interface/i18n/en.json +3463 -200
- package/neural-interface/i18n/pt-BR.json +3411 -151
- package/neural-interface/lib/assistant-api.js +127 -28
- package/neural-interface/lib/assistant-brains/claude.js +4 -3
- package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
- package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
- package/neural-interface/lib/assistant-brains/codex.js +9 -5
- package/neural-interface/lib/assistant-brains/opencode.js +120 -54
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
- package/neural-interface/lib/assistant-catalog.js +6 -7
- package/neural-interface/lib/assistant-dispatch.js +871 -156
- package/neural-interface/lib/assistant-envelope.js +3 -1
- package/neural-interface/lib/assistant-persona.js +80 -11
- package/neural-interface/lib/assistant-plan-permissions.js +10 -9
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
- package/neural-interface/lib/assistant-route-gate.js +6 -4
- package/neural-interface/lib/assistant-router.js +13 -8
- package/neural-interface/lib/assistant-runtime.js +56 -18
- package/neural-interface/lib/assistant-task-prompt.js +78 -14
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/backup-service.js +620 -3
- package/neural-interface/lib/backup-zip-reader.js +10 -5
- package/neural-interface/lib/backup-zip-writer.js +257 -0
- package/neural-interface/lib/browser-execution.js +10 -2
- package/neural-interface/lib/browser-guard-cdp.js +83 -0
- package/neural-interface/lib/browser-network-guard.js +284 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +186 -31
- package/neural-interface/lib/claude-agent-bridge.js +4 -2
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install.js +18 -138
- package/neural-interface/lib/cli-resolver.js +4 -1
- package/neural-interface/lib/cli-update-planner.js +32 -31
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-worker-app-server.js +172 -0
- package/neural-interface/lib/confined-fs.js +30 -3
- package/neural-interface/lib/crash-notice-api.js +98 -0
- package/neural-interface/lib/editor-completion-opencode.js +68 -68
- package/neural-interface/lib/exec-loop-command.js +4 -8
- package/neural-interface/lib/http-guards.js +8 -2
- package/neural-interface/lib/install-details.js +4 -1
- package/neural-interface/lib/mcp-agent-servers.js +67 -0
- package/neural-interface/lib/mcp-client-config.js +0 -29
- package/neural-interface/lib/mcp-external-servers.js +323 -0
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/morelogin.js +30 -0
- package/neural-interface/lib/native-loop-providers.js +547 -47
- package/neural-interface/lib/native-loop-runtime.js +268 -41
- package/neural-interface/lib/opencode-config.js +557 -0
- package/neural-interface/lib/opencode-db-schema.js +17 -34
- package/neural-interface/lib/opencode-history.js +48 -218
- package/neural-interface/lib/opencode-mcp-ready.js +108 -0
- package/neural-interface/lib/opencode-native-boundary.js +44 -0
- package/neural-interface/lib/opencode-protocol.js +249 -112
- package/neural-interface/lib/opencode-runtime-config.js +32 -22
- package/neural-interface/lib/opencode-serve-records.js +39 -21
- package/neural-interface/lib/opencode-v2-client.js +48 -153
- package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
- package/neural-interface/lib/opencode-worker-data.js +27 -0
- package/neural-interface/lib/opencode2-client.js +73 -37
- package/neural-interface/lib/opencode2-proxy.js +23 -63
- package/neural-interface/lib/opencode2-translate.js +51 -2
- package/neural-interface/lib/owner-authority.js +249 -0
- package/neural-interface/lib/page-session.js +275 -0
- package/neural-interface/lib/peer-identity.js +763 -0
- package/neural-interface/lib/request-gate.js +17 -2
- package/neural-interface/lib/rulesets/api.js +1 -1
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +92 -27
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +17 -30
- package/neural-interface/lib/session-title-generator.js +58 -36
- package/neural-interface/lib/skills/api-v2.js +495 -0
- package/neural-interface/lib/skills/bundle.js +418 -0
- package/neural-interface/lib/skills/catalog.js +294 -0
- package/neural-interface/lib/skills/creation-api.js +191 -0
- package/neural-interface/lib/skills/creation-brief.js +247 -0
- package/neural-interface/lib/skills/creation-sources.js +267 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +564 -0
- package/neural-interface/lib/skills/digest.js +119 -0
- package/neural-interface/lib/skills/discover.js +111 -0
- package/neural-interface/lib/skills/draft-confinement.js +214 -0
- package/neural-interface/lib/skills/eval-confinement.js +397 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +119 -0
- package/neural-interface/lib/skills/evals-api.js +212 -0
- package/neural-interface/lib/skills/evals.js +1573 -0
- package/neural-interface/lib/skills/export.js +200 -0
- package/neural-interface/lib/skills/frontmatter.js +64 -0
- package/neural-interface/lib/skills/health.js +75 -0
- package/neural-interface/lib/skills/json-edit.js +165 -0
- package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
- package/neural-interface/lib/skills/known-bridges.js +96 -0
- package/neural-interface/lib/skills/library.js +823 -0
- package/neural-interface/lib/skills/local-store.js +21 -0
- package/neural-interface/lib/skills/migration.js +433 -0
- package/neural-interface/lib/skills/operations.js +45 -0
- package/neural-interface/lib/skills/ownership.js +427 -0
- package/neural-interface/lib/skills/packages.js +88 -0
- package/neural-interface/lib/skills/reviews.js +212 -0
- package/neural-interface/lib/skills/revision.js +80 -0
- package/neural-interface/lib/skills/roots.js +262 -0
- package/neural-interface/lib/skills/runtime-checks.js +355 -0
- package/neural-interface/lib/skills/scan-assets.js +252 -0
- package/neural-interface/lib/skills/scan-context.js +26 -0
- package/neural-interface/lib/skills/scan-shell.js +324 -0
- package/neural-interface/lib/skills/scan.js +364 -0
- package/neural-interface/lib/skills/scanners.js +54 -0
- package/neural-interface/lib/skills/service.js +522 -0
- package/neural-interface/lib/skills/telemetry.js +113 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/whatsapp/bridge.js +6 -3
- package/neural-interface/lib/worker-confinement.js +1192 -0
- package/neural-interface/lib/worker-guard-setting.js +56 -0
- package/neural-interface/lib/worker-mcp-channel.js +175 -0
- package/neural-interface/lib/worker-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +229 -0
- package/neural-interface/lib/worker-sandbox.js +299 -0
- package/neural-interface/package-lock.json +18 -12
- package/neural-interface/package.json +2 -2
- package/neural-interface/public/index.html +3 -1
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +2 -2
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +35 -19
- package/neural-interface/public/shared/api.js +1527 -1336
- package/neural-interface/public/shared/app-frame.js +192 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +33 -2
- package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
- package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
- package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +49 -35
- package/neural-interface/public/shared/assistant/asst-control.js +157 -26
- package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
- package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
- package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
- package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
- package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
- package/neural-interface/public/shared/assistant/asst-panel.js +106 -11
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
- package/neural-interface/public/shared/assistant/asst-render.js +401 -77
- package/neural-interface/public/shared/assistant/asst-route.js +83 -12
- package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
- package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
- package/neural-interface/public/shared/assistant/asst-state.js +11 -15
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
- package/neural-interface/public/shared/automation-context.js +5 -2
- package/neural-interface/public/shared/cc-dropdown.js +89 -19
- package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
- package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
- package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
- package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
- package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
- package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
- package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +11 -3
- package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-view.js +4 -8
- package/neural-interface/public/shared/cli-status.js +2 -2
- package/neural-interface/public/shared/constants.js +2 -2
- package/neural-interface/public/shared/context-menu-areas.js +34 -0
- package/neural-interface/public/shared/context-menu-logic.js +243 -0
- package/neural-interface/public/shared/context-menu-model.js +366 -0
- package/neural-interface/public/shared/context-menu.js +372 -0
- package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
- package/neural-interface/public/shared/crash-notice-view.js +168 -0
- package/neural-interface/public/shared/desktop-update-view.js +20 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- package/neural-interface/public/shared/html-shell.js +9 -11
- package/neural-interface/public/shared/i18n.js +2 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +47 -252
- package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +6 -21
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
- package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
- package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +0 -21
- package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +14 -47
- package/neural-interface/public/shared/page-session.js +96 -0
- package/neural-interface/public/shared/registry.js +5 -3
- package/neural-interface/public/shared/retired-2d.js +3 -0
- package/neural-interface/public/shared/schedules-view.js +195 -61
- package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
- package/neural-interface/public/shared/settings/settings-data.js +3 -1
- package/neural-interface/public/shared/settings/settings-ia.js +6 -6
- package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
- package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
- package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
- package/neural-interface/public/shared/skills/ss-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +219 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
- package/neural-interface/public/shared/skills/ss-doc.js +173 -0
- package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
- package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
- package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
- package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
- package/neural-interface/public/shared/skills/ss-evals.js +417 -0
- package/neural-interface/public/shared/skills/ss-form.js +159 -0
- package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
- package/neural-interface/public/shared/skills/ss-ids.js +32 -0
- package/neural-interface/public/shared/skills/ss-lint.js +141 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
- package/neural-interface/public/shared/skills/ss-resources.js +165 -0
- package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
- package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
- package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
- package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
- package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
- package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
- package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
- package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
- package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
- package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
- package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
- package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
- package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
- package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
- package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
- package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
- package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
- package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
- package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
- package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
- package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
- package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
- package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
- package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
- package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
- package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
- package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +1525 -1779
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- package/neural-interface/public/shared/tool-list-picker.js +55 -0
- package/neural-interface/public/shared/tool-list-storage.js +24 -0
- package/neural-interface/public/shared/tool-list-view.js +112 -0
- package/neural-interface/public/shared/tour/tour-actor.js +268 -0
- package/neural-interface/public/shared/tour/tour-beats.js +336 -0
- package/neural-interface/public/shared/tour/tour-engine.js +884 -0
- package/neural-interface/public/shared/tour/tour-ink.js +390 -0
- package/neural-interface/public/shared/tour/tour-stage.js +440 -0
- package/neural-interface/public/shared/tour/tour-state.js +40 -0
- package/neural-interface/public/shared/tour/tour-tips.js +99 -0
- package/neural-interface/public/shared/tour/tour.css +144 -0
- package/neural-interface/public/shared/ui-automation-studio.js +91 -128
- package/neural-interface/public/shared/ui-claude-panel.js +77 -5
- package/neural-interface/public/shared/ui-command-runner.js +6 -3
- package/neural-interface/public/shared/ui-context-menu.js +295 -0
- package/neural-interface/public/shared/ui-cost-widget.js +57 -59
- package/neural-interface/public/shared/ui-crash-notice.js +187 -0
- package/neural-interface/public/shared/ui-detail.js +15 -3
- package/neural-interface/public/shared/ui-image-gallery.js +50 -47
- package/neural-interface/public/shared/ui-keybinds.js +26 -1
- package/neural-interface/public/shared/ui-menubar.js +8 -18
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
- package/neural-interface/public/shared/ui-navbar.js +4 -24
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
- package/neural-interface/public/shared/ui-sessions.js +5 -3
- package/neural-interface/public/shared/ui-settings.js +594 -462
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
- package/neural-interface/public/shared/ui-skills.js +33 -2830
- package/neural-interface/public/shared/ui-styleguide.js +5 -2
- package/neural-interface/public/shared/ui-sync.js +15 -5
- package/neural-interface/public/shared/ui-terminal.js +124 -180
- package/neural-interface/public/shared/ui-tool-updates.js +3 -1
- package/neural-interface/public/shared/ui-tooltip.js +10 -5
- package/neural-interface/public/shared/ui-trash.js +6 -4
- package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
- package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
- package/neural-interface/public/shared/ui-tutorial.css +47 -100
- package/neural-interface/public/shared/ui-tutorial.js +285 -509
- package/neural-interface/public/shared/ui-update.js +38 -2
- package/neural-interface/public/shared/ui-whiteboard.js +311 -58
- package/neural-interface/public/shared/ui-worker-end.js +4 -0
- package/neural-interface/public/shared/ui-workspaces.js +3 -2
- package/neural-interface/public/shared/view-variant.js +66 -0
- package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
- package/neural-interface/public/shared/whiteboard-list.js +172 -0
- package/neural-interface/public/shared/worker-end.js +10 -3
- package/neural-interface/public/shared/worker-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- package/neural-interface/public/variant/2d/background.js +2 -1
- package/neural-interface/public/variant/2d/context-menu.js +8 -2
- package/neural-interface/public/variant/2d/graph.js +42 -20
- package/neural-interface/public/variant/2d/hulls.js +14 -9
- package/neural-interface/public/variant/2d/lasso.js +11 -7
- package/neural-interface/public/variant/2d/main.js +32 -1
- package/neural-interface/public/variant/2d/minimap.js +11 -13
- package/neural-interface/public/variant/3d/main.js +69 -6
- package/neural-interface/public/variant/3d/map-controls.js +24 -9
- package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat.css +17 -0
- package/neural-interface/public/vendor/caveat/manifest.json +12 -0
- package/neural-interface/public/vendor/yaml/LICENSE +13 -0
- package/neural-interface/public/vendor/yaml/manifest.json +4 -0
- package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
- package/neural-interface/scripts/build-vendor.mjs +32 -0
- package/neural-interface/server.js +3934 -2454
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +18 -12
- package/package.json +16 -3
- package/postinstall.js +9 -4
- package/preuninstall.js +1 -1
- package/setup.js +97 -322
- package/skills/skill-forge/SKILL.md +177 -0
- package/skills/skill-forge/evals/evals.json +54 -0
- package/skills/skill-forge/references/package.md +105 -0
- package/skills/skill-forge/references/review-and-recovery.md +63 -0
- package/skills/skill-forge/references/runtime-profiles.md +20 -0
- package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
- package/skills/synabun/SKILL.md +10 -7
- package/skills/synabun/modules/audit.md +1 -5
- package/skills/synabun/modules/changelog.md +1 -5
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/memorize.md +6 -9
- package/skills/synabun/modules/schedule.md +4 -4
- package/mcp-server/dist/services/profiles.d.ts +0 -120
- package/mcp-server/dist/services/profiles.d.ts.map +0 -1
- package/mcp-server/dist/services/profiles.js +0 -387
- package/mcp-server/dist/services/profiles.js.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
- package/mcp-server/dist/tools/leonardo.d.ts +0 -13
- package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo.js +0 -21
- package/mcp-server/dist/tools/leonardo.js.map +0 -1
- package/mcp-server/dist/tools/profile.d.ts +0 -17
- package/mcp-server/dist/tools/profile.d.ts.map +0 -1
- package/mcp-server/dist/tools/profile.js +0 -114
- package/mcp-server/dist/tools/profile.js.map +0 -1
- package/mcp-server/src/services/profiles.ts +0 -428
- package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
- package/mcp-server/src/tools/leonardo.ts +0 -30
- package/mcp-server/src/tools/profile.ts +0 -122
- package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
- package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
- package/neural-interface/lib/mcp-profile-servers.js +0 -120
- package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
- package/skills/leonardo/SKILL.md +0 -168
- package/skills/leonardo/icon.svg +0 -1
- package/skills/leonardo/modules/browser-guide.md +0 -378
- package/skills/leonardo/modules/image-prompter.md +0 -283
- package/skills/leonardo/modules/model-advisor.md +0 -100
- package/skills/leonardo/modules/prompt-library.md +0 -110
- package/skills/leonardo/modules/style-guide.md +0 -186
- package/skills/leonardo/modules/video-prompter.md +0 -317
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Assistant — follow-ups: what a finished turn offers, as items to click
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
// A brain that ends a turn with next steps, decisions or offers appends one
|
|
5
|
+
// fenced block after all its text (lib/assistant-persona.js, "Follow-ups"):
|
|
6
|
+
//
|
|
7
|
+
// ```synabun-followups
|
|
8
|
+
// {"multi": false, "items": [{"label": "Ship it", "send": "Ship 1.4.0 to npm now", "description": "…"}]}
|
|
9
|
+
// ```
|
|
10
|
+
//
|
|
11
|
+
// An optional "style" says how the mascot presents it (asst-followups-card.js):
|
|
12
|
+
// "next" (quick actions, one click sends), "decisions" (independent picks sent
|
|
13
|
+
// together: what "multi": true has always meant), "confirm" (one proposed
|
|
14
|
+
// action: go ahead, or not now) and "question" (2-4 answers, the recommended
|
|
15
|
+
// one first, plus a typed answer; "question" holds the question). A block
|
|
16
|
+
// without it is "decisions" when multi, else "next", so older replies draw as
|
|
17
|
+
// they always did.
|
|
18
|
+
// The panel never shows the block: splitFollowups takes it out of the text
|
|
19
|
+
// (while it streams too) and the transcript draws its items as buttons
|
|
20
|
+
// (asst-followups-card.js); a click sends `send` as the user's next message.
|
|
21
|
+
// A model that leaves the tag off (a json or bare fence) still gets buttons
|
|
22
|
+
// when that fence is the message's last and holds exactly the block's shape.
|
|
23
|
+
// A turn whose last message has no block but still ends in a list of options
|
|
24
|
+
// or an "I can X, or Y" offer gets items read from the prose (guessFollowups),
|
|
25
|
+
// best effort. Whether a set was answered is read from the transcript: the
|
|
26
|
+
// user's next message is one of its items, or several of them one per line
|
|
27
|
+
// (matchFollowupReply), so a reload draws the same state as the live panel.
|
|
28
|
+
// No imports and no DOM: the server strips the block too (stripFollowups).
|
|
29
|
+
|
|
30
|
+
export const FOLLOWUPS_FENCE = 'synabun-followups';
|
|
31
|
+
export const FOLLOWUP_LIMITS = Object.freeze({ items: 8, label: 80, send: 2000, description: 200, heuristicItem: 400, question: 240, answer: 300 });
|
|
32
|
+
/** How a set is presented: quick actions, independent picks, one proposed action, a question. */
|
|
33
|
+
export const FOLLOWUP_STYLES = Object.freeze(['next', 'decisions', 'confirm', 'question']);
|
|
34
|
+
// What a model may write for a style; anything else falls back to the legacy reading of "multi".
|
|
35
|
+
const STYLE_NAMES = Object.freeze({
|
|
36
|
+
next: 'next', 'next-steps': 'next', steps: 'next', actions: 'next',
|
|
37
|
+
decisions: 'decisions', decision: 'decisions', multi: 'decisions', checklist: 'decisions',
|
|
38
|
+
confirm: 'confirm', confirmation: 'confirm', proceed: 'confirm', approve: 'confirm', 'go-ahead': 'confirm',
|
|
39
|
+
question: 'question', ask: 'question', choice: 'question',
|
|
40
|
+
});
|
|
41
|
+
/** A style name as the block wrote it ("Next steps", "next_steps", "Question") → a FOLLOWUP_STYLES entry, or ''. */
|
|
42
|
+
export function followupStyleName(value) {
|
|
43
|
+
const key = String(value ?? '').trim().toLowerCase().replace(/[\s_]+/g, '-');
|
|
44
|
+
return Object.hasOwn(STYLE_NAMES, key) ? STYLE_NAMES[key] : '';
|
|
45
|
+
}
|
|
46
|
+
// "Postgres (Recommended)": the label the way AskUserQuestion asks for it; the card shows a badge instead.
|
|
47
|
+
const RECOMMENDED_RE = /\s*[([]\s*(?:recommended|recomendad[oa])\s*[)\]]\s*$/i;
|
|
48
|
+
|
|
49
|
+
function clip(value, max) {
|
|
50
|
+
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
51
|
+
return text.length > max ? `${text.slice(0, max - 1).trimEnd()}…` : text;
|
|
52
|
+
}
|
|
53
|
+
/** Inline markdown off a label or a message (bold, italics, code ticks, links). */
|
|
54
|
+
function plain(value) {
|
|
55
|
+
return String(value ?? '')
|
|
56
|
+
.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
|
|
57
|
+
.replace(/(\*\*|__)(.+?)\1/g, '$2')
|
|
58
|
+
.replace(/(^|[\s(])[*_]([^*_\s][^*_]*?)[*_](?=[\s).,;:!?]|$)/g, '$1$2')
|
|
59
|
+
.replace(/`([^`]+)`/g, '$1')
|
|
60
|
+
.trim();
|
|
61
|
+
}
|
|
62
|
+
const norm = (value) => String(value ?? '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
63
|
+
|
|
64
|
+
const FENCE_OPEN_RE = /^[ \t]*(`{3,}|~{3,})[ \t]*([^\n]*?)[ \t]*$/;
|
|
65
|
+
const FENCE_CLOSE_RE = /^[ \t]*(`{3,}|~{3,})[ \t]*$/;
|
|
66
|
+
// A fence a model may put the block in when it forgets the tag: none, or JSON's.
|
|
67
|
+
const PLAIN_LANG_RE = /^(?:json5?|jsonc)?$/;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The text's fenced blocks, in order: { start, end, lang, inner, closed, typed }. A fence opened inside
|
|
71
|
+
* another is that block's content, never a block (a ```markdown example of the format is not the format).
|
|
72
|
+
* `typed`: the opening line has no newline yet, so its info string may still be growing.
|
|
73
|
+
*/
|
|
74
|
+
function scanFences(src) {
|
|
75
|
+
const blocks = [];
|
|
76
|
+
let open = null;
|
|
77
|
+
let at = 0;
|
|
78
|
+
while (at <= src.length) {
|
|
79
|
+
const nl = src.indexOf('\n', at);
|
|
80
|
+
const lineEnd = nl < 0 ? src.length : nl;
|
|
81
|
+
const line = src.slice(at, lineEnd);
|
|
82
|
+
if (!open) {
|
|
83
|
+
const m = FENCE_OPEN_RE.exec(line);
|
|
84
|
+
if (m && !(m[1][0] === '`' && m[2].includes('`'))) {
|
|
85
|
+
open = { start: at, fence: m[1], lang: (m[2].split(/\s+/)[0] || '').toLowerCase(), contentStart: nl < 0 ? src.length : nl + 1, typed: nl < 0 };
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
const m = FENCE_CLOSE_RE.exec(line);
|
|
89
|
+
if (m && m[1][0] === open.fence[0] && m[1].length >= open.fence.length) {
|
|
90
|
+
blocks.push({ start: open.start, end: lineEnd, lang: open.lang, inner: src.slice(open.contentStart, at), closed: true, typed: false });
|
|
91
|
+
open = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (nl < 0) break;
|
|
95
|
+
at = nl + 1;
|
|
96
|
+
}
|
|
97
|
+
if (open) blocks.push({ start: open.start, end: src.length, lang: open.lang, inner: src.slice(open.contentStart), closed: false, typed: open.typed });
|
|
98
|
+
return blocks;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** The first characters of an untagged or json fence that may become a follow-ups object: hidden while they stream. */
|
|
102
|
+
function mayBecomeFollowups(inner) {
|
|
103
|
+
const src = inner.trim();
|
|
104
|
+
return !src || /^\{\s*(?:"[a-z_]*"?)?$/i.test(src) || /^\{\s*"(?:multi|multi_select|multiSelect|title|items)"/.test(src);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The text with `b` taken out: what came before it, then what came after it as its own paragraph. */
|
|
108
|
+
function without(src, b) {
|
|
109
|
+
return [src.slice(0, b.start).replace(/\s+$/, ''), src.slice(b.end).trim()].filter(Boolean).join('\n\n');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The text without its follow-ups block → { body, block, partial }.
|
|
114
|
+
* `block`: the parsed set ({ multi, items, title, source: 'block' }) or null.
|
|
115
|
+
* The block is the last fence tagged synabun-followups; with no such fence, the message's LAST
|
|
116
|
+
* fenced block when it is untagged or json and holds exactly the follow-ups shape
|
|
117
|
+
* ({"items": [{"label", "send"}, …]}, optional multi / title / description), so a model that
|
|
118
|
+
* forgot the tag still gets buttons and an ordinary code block is never taken.
|
|
119
|
+
* `streaming`: the text is still arriving, so a fence being typed at its very end ("```synab",
|
|
120
|
+
* "```json\n{\"multi\"") is hidden too (`partial`), and an open block hides what follows it.
|
|
121
|
+
*/
|
|
122
|
+
export function splitFollowups(text, { streaming = false } = {}) {
|
|
123
|
+
const src = String(text ?? '').replace(/\r\n?/g, '\n');
|
|
124
|
+
const blocks = scanFences(src);
|
|
125
|
+
const tagged = blocks.filter((b) => b.lang === FOLLOWUPS_FENCE).pop();
|
|
126
|
+
if (tagged) {
|
|
127
|
+
const partial = !tagged.closed && streaming;
|
|
128
|
+
return { body: without(src, tagged), block: partial ? null : parseFollowupsBlock(tagged.inner), partial };
|
|
129
|
+
}
|
|
130
|
+
const last = blocks[blocks.length - 1];
|
|
131
|
+
if (last && !last.closed && streaming) {
|
|
132
|
+
// An opening still being typed ("```synab"), or a json / bare fence whose content may be the block.
|
|
133
|
+
const typing = last.typed && !!last.lang && (FOLLOWUPS_FENCE.startsWith(last.lang) || 'json'.startsWith(last.lang));
|
|
134
|
+
if (typing || (PLAIN_LANG_RE.test(last.lang) && mayBecomeFollowups(last.inner))) {
|
|
135
|
+
return { body: without(src, last), block: null, partial: true };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (last && PLAIN_LANG_RE.test(last.lang) && (last.closed || !streaming)) {
|
|
139
|
+
const block = shapedFollowups(last.inner);
|
|
140
|
+
if (block) return { body: without(src, last), block, partial: false };
|
|
141
|
+
}
|
|
142
|
+
return { body: src, block: null, partial: false };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** The text a surface that cannot click (WhatsApp, a summary) keeps: the block left out. */
|
|
146
|
+
export function stripFollowups(text) {
|
|
147
|
+
return splitFollowups(text).body;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* A label as a card shows it: without a "(Recommended)" suffix, and whether it had one. Display only:
|
|
152
|
+
* a question card's answer keeps the label exactly as the brain wrote it.
|
|
153
|
+
*/
|
|
154
|
+
export function splitRecommended(label) {
|
|
155
|
+
const text = String(label ?? '');
|
|
156
|
+
if (!RECOMMENDED_RE.test(text)) return { label: text, recommended: false };
|
|
157
|
+
return { label: text.replace(RECOMMENDED_RE, '').trim() || text, recommended: true };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** `item` with `recommended: true` when it says so (the flag, or a "(Recommended)" label suffix, which goes). */
|
|
161
|
+
function withRecommended(item, flagged) {
|
|
162
|
+
const marked = RECOMMENDED_RE.test(item.label);
|
|
163
|
+
if (marked) item.label = item.label.replace(RECOMMENDED_RE, '').trim() || item.label;
|
|
164
|
+
if (flagged || marked) item.recommended = true;
|
|
165
|
+
return item;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function itemFrom(raw) {
|
|
169
|
+
const { label: maxLabel, send: maxSend, description: maxDesc } = FOLLOWUP_LIMITS;
|
|
170
|
+
if (typeof raw === 'string') {
|
|
171
|
+
const text = plain(raw);
|
|
172
|
+
if (!text) return null;
|
|
173
|
+
const item = withRecommended({ label: clip(shortLabel(text), maxLabel), send: clip(text, maxSend), description: '' }, false);
|
|
174
|
+
if (item.recommended) item.send = clip(text.replace(RECOMMENDED_RE, '').trim() || text, maxSend);
|
|
175
|
+
return item;
|
|
176
|
+
}
|
|
177
|
+
if (!raw || typeof raw !== 'object') return null;
|
|
178
|
+
const label = plain(raw.label ?? raw.title ?? raw.text ?? raw.name ?? '');
|
|
179
|
+
const send = plain(raw.send ?? raw.message ?? raw.prompt ?? raw.reply ?? raw.value ?? raw.text ?? label);
|
|
180
|
+
if (!label && !send) return null;
|
|
181
|
+
return withRecommended({
|
|
182
|
+
label: clip(label || shortLabel(send), maxLabel),
|
|
183
|
+
send: clip(send || label, maxSend),
|
|
184
|
+
description: clip(plain(raw.description ?? raw.desc ?? raw.detail ?? raw.hint ?? ''), maxDesc),
|
|
185
|
+
}, raw.recommended === true || raw.recommended === 'true');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function itemsFrom(list) {
|
|
189
|
+
const seen = new Set();
|
|
190
|
+
const out = [];
|
|
191
|
+
for (const raw of Array.isArray(list) ? list : []) {
|
|
192
|
+
const item = itemFrom(raw);
|
|
193
|
+
if (!item || seen.has(norm(item.send))) continue;
|
|
194
|
+
seen.add(norm(item.send));
|
|
195
|
+
out.push(item);
|
|
196
|
+
if (out.length >= FOLLOWUP_LIMITS.items) break;
|
|
197
|
+
}
|
|
198
|
+
return out;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const LIST_ITEM_RE = /^\s*(?:[-*•+]|\(?\d{1,2}[.)])\s+(.+)$/;
|
|
202
|
+
|
|
203
|
+
/** A block that is no JSON: one item per list line ("- Ship it", "1. Ship it"). */
|
|
204
|
+
function linesToItems(src) {
|
|
205
|
+
const lines = src.split('\n').map((line) => LIST_ITEM_RE.exec(line)?.[1] || '').filter(Boolean);
|
|
206
|
+
return lines.length ? lines : null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** JSON, a trailing comma forgiven; undefined when it is none. */
|
|
210
|
+
function parseJson(src) {
|
|
211
|
+
for (const candidate of [src, src.replace(/,\s*([}\]])/g, '$1')]) {
|
|
212
|
+
try { return JSON.parse(candidate); } catch { /* the next candidate */ }
|
|
213
|
+
}
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** The fenced block's content → { multi, items, title, source: 'block' } or null. */
|
|
218
|
+
export function parseFollowupsBlock(raw) {
|
|
219
|
+
const src = String(raw ?? '').trim();
|
|
220
|
+
if (!src) return null;
|
|
221
|
+
let data = parseJson(src);
|
|
222
|
+
if (data === undefined) data = linesToItems(src);
|
|
223
|
+
return setFrom(data);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const filled = (value) => typeof value === 'string' && value.trim() !== '';
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* An untagged or json fence's content as a set, only in the exact follow-ups shape: an object whose
|
|
230
|
+
* `items` are all objects with a `label` and a `send`. Anything else is an ordinary code block.
|
|
231
|
+
*/
|
|
232
|
+
function shapedFollowups(raw) {
|
|
233
|
+
const data = parseJson(String(raw ?? '').trim());
|
|
234
|
+
if (!data || typeof data !== 'object' || Array.isArray(data) || !Array.isArray(data.items) || !data.items.length) return null;
|
|
235
|
+
if (!data.items.every((item) => item && typeof item === 'object' && filled(item.label) && filled(item.send))) return null;
|
|
236
|
+
return setFrom(data);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The style a set is drawn in: the block's own when it fits its items, else what "multi" always meant.
|
|
241
|
+
* decisions needs two items to pick from; confirm is one proposed action and at most one alternative.
|
|
242
|
+
*/
|
|
243
|
+
function resolveStyle(named, multi, count) {
|
|
244
|
+
let style = named || (multi ? 'decisions' : 'next');
|
|
245
|
+
if (style === 'decisions' && count < 2) style = 'next';
|
|
246
|
+
if (style === 'confirm' && count > 2) style = 'next';
|
|
247
|
+
return style;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Parsed block content (an object or a list) → { style, multi, items, title, question, source: 'block' } or null. */
|
|
251
|
+
function setFrom(data) {
|
|
252
|
+
const list = Array.isArray(data) ? data
|
|
253
|
+
: Array.isArray(data?.items) ? data.items
|
|
254
|
+
: Array.isArray(data?.followups) ? data.followups
|
|
255
|
+
: Array.isArray(data?.options) ? data.options : null;
|
|
256
|
+
const items = itemsFrom(list);
|
|
257
|
+
if (!items.length) return null;
|
|
258
|
+
const obj = data && !Array.isArray(data) && typeof data === 'object' ? data : {};
|
|
259
|
+
const multi = obj.multi === true || obj.multi_select === true || obj.multiSelect === true || obj.multiple === true;
|
|
260
|
+
const style = resolveStyle(followupStyleName(obj.style), multi, items.length);
|
|
261
|
+
const title = clip(plain(obj.title ?? obj.prompt ?? ''), 80);
|
|
262
|
+
// A question's own words: "question", else the title (a question block that put it there).
|
|
263
|
+
const question = style === 'question' ? clip(plain(obj.question ?? obj.ask ?? obj.title ?? obj.prompt ?? ''), FOLLOWUP_LIMITS.question) : '';
|
|
264
|
+
return { style, multi: style === 'decisions', items, title, question, source: 'block' };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** "**Ship it** — publishes 1.4.0" → "Ship it": a bold lead, else the words before " — " / ": ", else the first ~8 words. */
|
|
268
|
+
function shortLabel(text) {
|
|
269
|
+
const src = String(text ?? '').trim();
|
|
270
|
+
const bold = /^\s*(?:\*\*|__)(.+?)(?:\*\*|__)/.exec(src);
|
|
271
|
+
if (bold) return plain(bold[1]).replace(/[:.\-–—]+$/, '').trim();
|
|
272
|
+
const flat = plain(src);
|
|
273
|
+
const cut = /^(.{3,60}?)\s*(?:\s[—–-]\s|:\s)/.exec(flat);
|
|
274
|
+
if (cut) return cut[1].trim();
|
|
275
|
+
const words = flat.split(/\s+/);
|
|
276
|
+
return words.length <= 8 ? flat.replace(/[.;]+$/, '') : `${words.slice(0, 8).join(' ')}…`;
|
|
277
|
+
}
|
|
278
|
+
/** What follows the label, as its one-line description. */
|
|
279
|
+
function restOf(text, label) {
|
|
280
|
+
const flat = plain(text);
|
|
281
|
+
const lead = flat.indexOf(label.replace(/…$/, ''));
|
|
282
|
+
const rest = lead === 0 ? flat.slice(label.replace(/…$/, '').length) : '';
|
|
283
|
+
return clip(rest.replace(/^\s*[:\-–—.]+\s*/, ''), FOLLOWUP_LIMITS.description);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// A list or an offer is a follow-up only when the text says it asks: words of choice or a question.
|
|
287
|
+
const OFFER_HEADER_RE = /\b(?:next steps?|follow[- ]?ups?|options?|choices?|decisions?|decide|choose|pick|which|want me to|would you like|do you want|should i|shall i|let me know|tell me|reply with|i can|i could|we can|you can|prefer|your call|up to you|need from you|ready to|suggest(?:ed|ions?)?|recommend(?:ed|ations?)?)\b/i;
|
|
288
|
+
const MULTI_HEADER_RE = /\b(?:numbers|decisions|any of|all that apply|pick (?:any|several|one or more)|one or more|as many|each)\b/i;
|
|
289
|
+
|
|
290
|
+
/** A list that ends the text, under a line that asks: its items. */
|
|
291
|
+
function trailingList(src) {
|
|
292
|
+
const lines = src.split('\n');
|
|
293
|
+
let i = lines.length - 1;
|
|
294
|
+
while (i >= 0 && !lines[i].trim()) i -= 1;
|
|
295
|
+
const raw = [];
|
|
296
|
+
let current = [];
|
|
297
|
+
let numbered = 0;
|
|
298
|
+
for (; i >= 0; i -= 1) {
|
|
299
|
+
const line = lines[i];
|
|
300
|
+
if (!line.trim()) {
|
|
301
|
+
// A blank line inside a loose list: the list goes on when an item is above it.
|
|
302
|
+
let j = i - 1;
|
|
303
|
+
while (j >= 0 && !lines[j].trim()) j -= 1;
|
|
304
|
+
if (j >= 0 && (LIST_ITEM_RE.test(lines[j]) || /^\s{2,}\S/.test(lines[j])) && raw.length) { i = j + 1; continue; }
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
const item = LIST_ITEM_RE.exec(line);
|
|
308
|
+
if (item) {
|
|
309
|
+
raw.unshift([item[1], ...current].join(' '));
|
|
310
|
+
current = [];
|
|
311
|
+
if (/^\s*\(?\d/.test(line)) numbered += 1;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (/^\s{2,}\S/.test(line)) { current.unshift(line.trim()); continue; } // an item's continuation line
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
if (current.length || raw.length < 2 || raw.length > FOLLOWUP_LIMITS.items) return null;
|
|
318
|
+
if (raw.some((text) => text.length > FOLLOWUP_LIMITS.heuristicItem)) return null;
|
|
319
|
+
let h = i;
|
|
320
|
+
while (h >= 0 && !lines[h].trim()) h -= 1;
|
|
321
|
+
const header = h >= 0 ? lines[h].trim() : '';
|
|
322
|
+
if (!header || header.length > 300 || !(OFFER_HEADER_RE.test(header) || /\?\s*\**\s*$/.test(header))) return null;
|
|
323
|
+
const items = raw.map((text) => {
|
|
324
|
+
const label = clip(shortLabel(text), FOLLOWUP_LIMITS.label);
|
|
325
|
+
return { label, send: clip(plain(text), FOLLOWUP_LIMITS.send), description: restOf(text, label) };
|
|
326
|
+
});
|
|
327
|
+
const multi = numbered === raw.length && MULTI_HEADER_RE.test(header);
|
|
328
|
+
return { style: multi ? 'decisions' : 'next', multi, items, title: clip(plain(header).replace(/[:]\s*$/, ''), 80), question: '', source: 'heuristic' };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const OFFER_RE = /^(?:(?:but |and |so |or )?(?:i can|i could|we can|i'm happy to|happy to|want me to|do you want me to|would you like me to|should i|shall i))\s+(.+?)\s*[.?!]*$/i;
|
|
332
|
+
const ASKS_RE = /^(?:(?:but |and |so )?(?:want me to|do you want me to|would you like me to|should i|shall i))\s/i;
|
|
333
|
+
const capital = (s) => (s ? s[0].toUpperCase() + s.slice(1) : s);
|
|
334
|
+
|
|
335
|
+
/** "I can re-run it after the reset, or now on Claude…" → its alternatives; "Want me to ship it?" → one yes. */
|
|
336
|
+
function orOffer(src) {
|
|
337
|
+
const paragraphs = src.split(/\n\s*\n/).map((p) => p.trim()).filter(Boolean);
|
|
338
|
+
const last = paragraphs[paragraphs.length - 1] || '';
|
|
339
|
+
if (!last || last.length > 600 || LIST_ITEM_RE.test(last.split('\n').pop())) return null;
|
|
340
|
+
const sentences = plain(last).replace(/\s+/g, ' ').split(/(?<=[.?!])\s+(?=[A-Z"'(])/);
|
|
341
|
+
const sentence = sentences[sentences.length - 1] || '';
|
|
342
|
+
const m = OFFER_RE.exec(sentence);
|
|
343
|
+
if (!m) return null;
|
|
344
|
+
const rest = m[1].trim();
|
|
345
|
+
const parts = rest.split(/\s*,?\s+\bor\b\s+/i).map((p) => p.replace(/^(?:else|instead)\s+/i, '').trim()).filter(Boolean);
|
|
346
|
+
if (parts.length >= 2 && parts.length <= 4 && parts.every((p) => p.split(/\s+/).length >= 2)) {
|
|
347
|
+
const items = parts.map((p) => { const send = clip(capital(p), FOLLOWUP_LIMITS.send); return { label: clip(shortLabel(send), FOLLOWUP_LIMITS.label), send, description: '' }; });
|
|
348
|
+
return { style: 'next', multi: false, items, title: '', question: '', source: 'heuristic' };
|
|
349
|
+
}
|
|
350
|
+
if (ASKS_RE.test(sentence) && rest.split(/\s+/).length >= 2) {
|
|
351
|
+
// "Want me to open a PR?": one proposed action, so a confirm (go ahead, or not now).
|
|
352
|
+
const send = clip(`Yes, ${rest.replace(/[.?!]+$/, '')}`, FOLLOWUP_LIMITS.send);
|
|
353
|
+
return { style: 'confirm', multi: false, items: [{ label: clip(send, FOLLOWUP_LIMITS.label), send, description: '' }], title: clip(sentence, 80), question: '', source: 'heuristic' };
|
|
354
|
+
}
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Best-effort follow-ups of a finished message that has no block: a list of options that ends it,
|
|
360
|
+
* under a line that asks ("Decisions I need from you (reply with the numbers):"), or an offer in
|
|
361
|
+
* its last sentence ("I can X, or Y.", "Want me to X?"). null when the text offers nothing.
|
|
362
|
+
*/
|
|
363
|
+
export function guessFollowups(text) {
|
|
364
|
+
const src = String(text ?? '').replace(/\r\n?/g, '\n').trim();
|
|
365
|
+
if (!src) return null;
|
|
366
|
+
const split = splitFollowups(src);
|
|
367
|
+
if (split.block) return null;
|
|
368
|
+
// A code block that ends the message is output, not options.
|
|
369
|
+
if (/```\s*$/.test(split.body)) return null;
|
|
370
|
+
return trailingList(split.body) || orOffer(split.body);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** The message a choice sends: one item's `send`, or several of them, one per line. */
|
|
374
|
+
export function followupMessage(set, indices = []) {
|
|
375
|
+
const items = [...new Set(indices)].sort((a, b) => a - b).map((i) => set?.items?.[i]).filter(Boolean);
|
|
376
|
+
if (!items.length) return '';
|
|
377
|
+
if (items.length === 1) return items[0].send;
|
|
378
|
+
return items.map((item) => `- ${item.send}`).join('\n');
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* A confirm with one proposed action gets a way to say no: `hold` ({ label, send }, the card's own words)
|
|
383
|
+
* as its second item, so a "Not now" is a message too and reads back from the transcript like any pick.
|
|
384
|
+
*/
|
|
385
|
+
export function confirmSet(set, hold) {
|
|
386
|
+
if (set?.style !== 'confirm' || !Array.isArray(set.items) || set.items.length !== 1 || !hold?.send) return set;
|
|
387
|
+
return { ...set, items: [...set.items, { label: String(hold.label || hold.send), send: String(hold.send), description: '', hold: true }] };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* What the user's next message `text` did to an offered set: { state: 'answered', indices, text } or
|
|
392
|
+
* { state: 'stale', indices: [] }. One of its items (several, one per line, for decisions) answers any
|
|
393
|
+
* set; for a question, whatever the user says next is their answer (`text`, the words they typed),
|
|
394
|
+
* except a slash command. The same rule live and from the journal, so a reload draws the same state.
|
|
395
|
+
*/
|
|
396
|
+
export function settleFollowups(set, text) {
|
|
397
|
+
const indices = matchFollowupReply(set, text);
|
|
398
|
+
if (indices) return { state: 'answered', indices, text: '' };
|
|
399
|
+
const typed = String(text ?? '').trim();
|
|
400
|
+
if (set?.style === 'question' && typed && !typed.startsWith('/')) return { state: 'answered', indices: [], text: clip(typed, FOLLOWUP_LIMITS.answer) };
|
|
401
|
+
return { state: 'stale', indices: [], text: '' };
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** Which items of `set` the user's message `text` chose (the indices), or null when it chose none of them. */
|
|
405
|
+
export function matchFollowupReply(set, text) {
|
|
406
|
+
const items = Array.isArray(set?.items) ? set.items : [];
|
|
407
|
+
if (!items.length) return null;
|
|
408
|
+
const whole = norm(text);
|
|
409
|
+
if (!whole) return null;
|
|
410
|
+
const single = items.findIndex((item) => norm(item.send) === whole);
|
|
411
|
+
if (single >= 0) return [single];
|
|
412
|
+
if (!set.multi) return null;
|
|
413
|
+
const lines = String(text ?? '').split('\n').map((line) => norm(line.replace(/^\s*[-*•]\s+/, ''))).filter(Boolean);
|
|
414
|
+
if (lines.length < 2) return null;
|
|
415
|
+
const picked = lines.map((line) => items.findIndex((item) => norm(item.send) === line));
|
|
416
|
+
return picked.every((i) => i >= 0) ? [...new Set(picked)].sort((a, b) => a - b) : null;
|
|
417
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// One countdown owner per mounted conversation. Transcript replay and run-card
|
|
2
|
+
// refreshes update keyed records; they never allocate their own waiting cards.
|
|
3
|
+
import { createLimitWait } from './asst-limit-wait.js';
|
|
4
|
+
|
|
5
|
+
export function createLimitDock(transcript, { t, createWait = createLimitWait, createElement = () => document.createElement('div') } = {}) {
|
|
6
|
+
const host = createElement();
|
|
7
|
+
host.className = 'asst-limit-dock';
|
|
8
|
+
host.hidden = true;
|
|
9
|
+
transcript.after(host);
|
|
10
|
+
const entries = new Map();
|
|
11
|
+
let active = null;
|
|
12
|
+
let wait = null;
|
|
13
|
+
let provider = null;
|
|
14
|
+
let destroyed = false;
|
|
15
|
+
function render() {
|
|
16
|
+
const next = [...entries.entries()].sort((a, b) => (b[1].since || 0) - (a[1].since || 0))[0];
|
|
17
|
+
const key = next?.[0] || null;
|
|
18
|
+
if (key !== active || next?.[1].provider !== provider) {
|
|
19
|
+
wait?.destroy();
|
|
20
|
+
host.replaceChildren();
|
|
21
|
+
active = key;
|
|
22
|
+
provider = next?.[1].provider;
|
|
23
|
+
wait = next ? createWait({ t, ...next[1], onRetry: next[1].onRetry ? () => entries.get(key)?.onRetry?.() : null }) : null;
|
|
24
|
+
if (wait) host.appendChild(wait.el);
|
|
25
|
+
} else if (wait) wait.update({ resetsAt: next[1].resetsAt });
|
|
26
|
+
host.hidden = !wait;
|
|
27
|
+
return wait;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
el: host,
|
|
31
|
+
set(key, info) {
|
|
32
|
+
if (destroyed) return null;
|
|
33
|
+
if (info) entries.set(key, { ...info, since: entries.get(key)?.since || info.since });
|
|
34
|
+
else entries.delete(key);
|
|
35
|
+
return render();
|
|
36
|
+
},
|
|
37
|
+
clear() { if (destroyed) return; entries.clear(); render(); },
|
|
38
|
+
destroy() { destroyed = true; entries.clear(); wait?.destroy(); wait = null; active = null; host.remove(); },
|
|
39
|
+
};
|
|
40
|
+
}
|