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
|
@@ -67,15 +67,18 @@ export const ADMIN_ONLY_PREFIXES = Object.freeze([
|
|
|
67
67
|
'/api/settings', '/api/display-settings',
|
|
68
68
|
'/api/connections', '/api/setup', '/api/system',
|
|
69
69
|
'/api/invite/key', '/api/invite/sessions', '/api/invite/proxy', '/api/invite/permissions',
|
|
70
|
-
'/api/claude-code', '/api/mcp-key', '/api/mcp/runtime-
|
|
70
|
+
'/api/claude-code', '/api/mcp-key', '/api/mcp/runtime-tools',
|
|
71
71
|
// MCP servers and plugins as the owner set them up: their credentials (GET /api/mcp/env/<name>?reveal=1
|
|
72
72
|
// answers with them), the registry of the commands that are started, installs, what is written into the
|
|
73
|
-
// CLIs' own configuration, and
|
|
73
|
+
// CLIs' own configuration, and tool lists. Everything under /api/mcp, the two entries above included.
|
|
74
74
|
'/api/mcp',
|
|
75
75
|
'/api/plugins',
|
|
76
76
|
// Installing a skill into one of the owner's runtimes, or removing it (Settings; its older address is under
|
|
77
77
|
// /api/claude-code above). The "Skills Studio" permission opens /api/skills-studio, not this.
|
|
78
78
|
'/api/skills/install',
|
|
79
|
+
// The Skills Service's installs, rollbacks and removals, the move of the old bridges, and its settings: the
|
|
80
|
+
// owner's, from the app's own page (lib/skills/api-v2.js refuses guests and agents on its own too).
|
|
81
|
+
'/api/skills-studio/v2/installations', '/api/skills-studio/v2/migrations', '/api/skills-studio/v2/config',
|
|
79
82
|
'/api/tunnel/start', '/api/tunnel/stop',
|
|
80
83
|
'/api/bridges', '/api/keybinds',
|
|
81
84
|
'/api/cli',
|
|
@@ -124,6 +127,13 @@ export const FEATURE_PERMISSION_MAP = Object.freeze([
|
|
|
124
127
|
{ prefix: '/api/style-guide', perm: 'styleGuide' },
|
|
125
128
|
].map(Object.freeze));
|
|
126
129
|
|
|
130
|
+
/**
|
|
131
|
+
* What an invite guest only reads, whatever the owner opened to guests: the "Skills Studio" permission shows the
|
|
132
|
+
* skills, and every change (save, create, delete, files, icons, install, customize, import, the Skills Service's
|
|
133
|
+
* drafts and reviews) is the owner's, as lib/skills/api-v2.js and each legacy Studio route say on their own too.
|
|
134
|
+
*/
|
|
135
|
+
export const GUEST_READ_ONLY_PREFIXES = Object.freeze(['/api/skills-studio']);
|
|
136
|
+
|
|
127
137
|
/**
|
|
128
138
|
* The GETs a guest does not get for the asking. The terminal's lists of a folder's files and of its branches take
|
|
129
139
|
* any folder of the disk from the request, so they are the terminal's: a guest reads them with the permission
|
|
@@ -579,6 +589,11 @@ export function createRequestGates({
|
|
|
579
589
|
? FEATURE_READ_PATHS.find((entry) => read.some((form) => startsWithSegment(form, entry.path)))
|
|
580
590
|
: FEATURE_PERMISSION_MAP.find((entry) => read.some((form) => form.startsWith(entry.prefix)));
|
|
581
591
|
if (match && !permissions()?.[match.perm]) return res.status(403).json({ error: 'Feature not enabled' });
|
|
592
|
+
// And a feature opened to guests that they only read: no change, whatever was opened.
|
|
593
|
+
const reading = req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS';
|
|
594
|
+
if (!reading && GUEST_READ_ONLY_PREFIXES.some((prefix) => read.some((form) => startsWithSegment(form, prefix)))) {
|
|
595
|
+
return sendGuardError(res, 403, 'GUEST_FORBIDDEN', 'Guests can read skills, not change them.');
|
|
596
|
+
}
|
|
582
597
|
return next();
|
|
583
598
|
}
|
|
584
599
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
// lock file that cannot be used at all is `LOCK_UNAVAILABLE`, a 500: nothing
|
|
28
28
|
// was changed, and trying again will not help until the data home is fixed. A
|
|
29
29
|
// remove that leaves a copy to the user (an edited one, or for OpenCode one
|
|
30
|
-
// that the user's own line in
|
|
30
|
+
// that the user's own line in opencode.json points at) is not a refusal: 200,
|
|
31
31
|
// `ok: true`, `kept: [{path, reason}]`.
|
|
32
32
|
//
|
|
33
33
|
// Every installer call is synchronous and runs to its end inside the handler,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// ═══════════════════════════════════════════
|
|
4
4
|
//
|
|
5
5
|
// The rules installer edits files that belong to the user and to other CLIs
|
|
6
|
-
// (~/.codex/AGENTS.md, ~/.gemini/GEMINI.md, OpenCode's
|
|
6
|
+
// (~/.codex/AGENTS.md, ~/.gemini/GEMINI.md, OpenCode's opencode.json). These
|
|
7
7
|
// helpers make each write all-or-nothing, keep a symlinked file a symlink,
|
|
8
8
|
// refuse to write through a link that points at nothing, keep a copy of what
|
|
9
9
|
// was there before, never read a corrupt JSON file as `{}`, and never write
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// keeps an untouched copy current:
|
|
7
7
|
//
|
|
8
8
|
// claude owned file ~/.claude/rules/synabun.md (and each Claude account's config dir)
|
|
9
|
-
// opencode owned file <opencode config dir>/synabun.md + one `instructions` entry in
|
|
9
|
+
// opencode owned file <opencode config dir>/synabun.md + one `instructions` entry in opencode.json
|
|
10
10
|
// codex block ~/.codex/AGENTS.md (account homes link to it, or get their own block)
|
|
11
11
|
// gemini block ~/.gemini/GEMINI.md
|
|
12
12
|
// cursor none User Rules live inside the app: copy and paste ("manual")
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
// (the user opted out) or null (never asked). `created` lists the files and
|
|
30
30
|
// directories install made, `links` the symlinks it made and where each
|
|
31
31
|
// pointed. OpenCode's row also holds `instructionsEntry`, the exact string
|
|
32
|
-
// SynaBun added to
|
|
32
|
+
// SynaBun added to opencode.json. `removal` on a row is a removal that is not
|
|
33
33
|
// over: it is saved, with the paths it is about to take, before the first
|
|
34
34
|
// copy goes, and it stays when a copy was refused (damaged markers, an
|
|
35
35
|
// unreadable file). The next boot finishes it (reconcile), and the record is
|
|
@@ -74,6 +74,7 @@ import { loadLegacyHashes, normalise, renderRuleset, RULESET_HOSTS } from './ren
|
|
|
74
74
|
import { findBlocks, findLegacy, isPristine, removeBlock, removeRange, replaceRange, upsertBlock, wrap } from './managed-text.js';
|
|
75
75
|
import { backupFile, findBrokenLink, readBytes, readJsonStrict, unlinkIfUnchanged, writeTextAtomic } from './fs-safe.js';
|
|
76
76
|
import { addToJsonList, countJsonKey, removeFromJsonList } from './json-edit.js';
|
|
77
|
+
import { mcpServersOf, mergedOpencodeConfig, opencodeConfigDir, opencodeConfigFilePath, readOpencodeConfigDocuments } from '../opencode-config.js';
|
|
77
78
|
import { createWriterLock, DEFAULT_LOCK_TIMEOUT_MS, LOCK_FILE_NAME } from './lock.js';
|
|
78
79
|
|
|
79
80
|
export const RULESET_STATES = Object.freeze(['not-installed', 'installed', 'newer', 'outdated', 'modified', 'conflict', 'shadowed', 'error', 'manual']);
|
|
@@ -190,7 +191,7 @@ export function createRulesetInstaller({
|
|
|
190
191
|
// `hooks` are seams for tests, each a point where another program could act
|
|
191
192
|
// (or where a test holds an operation still, inside the lock):
|
|
192
193
|
// afterInspect(host, path, status) once a carrier was read and is about to be
|
|
193
|
-
// written, beforeCommit(path) right before a file (a carrier,
|
|
194
|
+
// written, beforeCommit(path) right before a file (a carrier, opencode.json,
|
|
194
195
|
// the state file) is checked against what was read and replaced or deleted,
|
|
195
196
|
// afterCheck(path) between that check and a delete, afterSave(revision) once
|
|
196
197
|
// the state file was saved.
|
|
@@ -351,12 +352,14 @@ export function createRulesetInstaller({
|
|
|
351
352
|
|
|
352
353
|
// ── Where things live ──────────────────────────────────────────────────────
|
|
353
354
|
// Resolved the way the MCP toggle routes resolve them: Claude, Codex and
|
|
354
|
-
// Gemini under the home directory, OpenCode
|
|
355
|
+
// Gemini under the home directory, OpenCode in its configuration folder
|
|
356
|
+
// (OPENCODE_CONFIG_DIR, else XDG_CONFIG_HOME/opencode, else ~/.config/opencode),
|
|
357
|
+
// whose opencode.json OpenCode 2 reads (lib/opencode-config.js).
|
|
355
358
|
function layout() {
|
|
356
359
|
// The server passes the resolver its /api/opencode/mcp toggle uses, so both always edit the same file.
|
|
357
360
|
const opencodeConfig = opencodeConfigPath
|
|
358
361
|
? resolve(opencodeConfigPath())
|
|
359
|
-
:
|
|
362
|
+
: opencodeConfigFilePath(opencodeConfigDir({ env: env || {}, home }));
|
|
360
363
|
return {
|
|
361
364
|
claudeDir: join(home, '.claude'),
|
|
362
365
|
codexHome: join(home, '.codex'),
|
|
@@ -427,7 +430,7 @@ export function createRulesetInstaller({
|
|
|
427
430
|
if (host === 'claude') return !!JSON.parse(readFileSync(join(home, '.claude.json'), 'utf8'))?.mcpServers?.SynaBun;
|
|
428
431
|
if (host === 'gemini') return !!JSON.parse(readFileSync(join(where.geminiDir, 'settings.json'), 'utf8'))?.mcpServers?.SynaBun;
|
|
429
432
|
if (host === 'codex') return /^\[mcp_servers\.SynaBun\]/m.test(readFileSync(join(where.codexHome, 'config.toml'), 'utf8'));
|
|
430
|
-
if (host === 'opencode') return !!
|
|
433
|
+
if (host === 'opencode') return !!mcpServersOf(mergedOpencodeConfig(readOpencodeConfigDocuments(where.opencodeDir))).SynaBun;
|
|
431
434
|
} catch { /* no config, no registration */ }
|
|
432
435
|
return false;
|
|
433
436
|
}
|
|
@@ -511,7 +514,7 @@ export function createRulesetInstaller({
|
|
|
511
514
|
/**
|
|
512
515
|
* The file an `instructions` entry of `configPath` names, or null when the entry is not one plain path: a URL, or
|
|
513
516
|
* a pattern with glob characters. A leading `~` is the installer's home, and a relative path starts at the
|
|
514
|
-
* directory of
|
|
517
|
+
* directory of opencode.json (never at this process's working directory).
|
|
515
518
|
*/
|
|
516
519
|
function entryFile(value, configPath) {
|
|
517
520
|
if (typeof value !== 'string' || !value) return null;
|
|
@@ -530,8 +533,7 @@ export function createRulesetInstaller({
|
|
|
530
533
|
if (path === resolve(owned)) return true;
|
|
531
534
|
try { return realpathSync(path) === realpathSync(owned); } catch { return false; }
|
|
532
535
|
}
|
|
533
|
-
function readOpencodeConfig() {
|
|
534
|
-
const path = layout().opencodeConfig;
|
|
536
|
+
function readOpencodeConfig(path = layout().opencodeConfig) {
|
|
535
537
|
const entry = opencodeEntry();
|
|
536
538
|
const refuse = (why) => ({ path, exists: true, hasEntry: false, cfg: null, text: null, bytes: null, error: `${path} ${why}. SynaBun will not rewrite it: fix the file, or add "${entry}" to its "instructions" list by hand.` });
|
|
537
539
|
let bytes;
|
|
@@ -558,7 +560,7 @@ export function createRulesetInstaller({
|
|
|
558
560
|
return { path, exists: true, hasEntry: (cfg.instructions || []).some((value) => entryIsOwnedFile(value, path)), cfg, text, bytes, error: null };
|
|
559
561
|
}
|
|
560
562
|
/**
|
|
561
|
-
* The text of
|
|
563
|
+
* The text of opencode.json after `edit()`, accepted only when it parses to
|
|
562
564
|
* exactly `cfg`. There is no other way to write this file: when the edit
|
|
563
565
|
* cannot be made on the text, SynaBun refuses rather than write the whole
|
|
564
566
|
* object out again and lose what the user's file spells its own way.
|
|
@@ -570,7 +572,7 @@ export function createRulesetInstaller({
|
|
|
570
572
|
} catch { /* text the editor could not follow */ }
|
|
571
573
|
throw new Error(`${path} could not be edited safely, so SynaBun did not write it. Change its "instructions" list by hand: the entry is "${opencodeEntry()}".`);
|
|
572
574
|
}
|
|
573
|
-
/** What install writes when there is no
|
|
575
|
+
/** What install writes when there is no opencode.json yet: remove deletes the file only while it still holds exactly this. */
|
|
574
576
|
function newConfigText(value) {
|
|
575
577
|
return addToJsonList('{}\n', 'instructions', value);
|
|
576
578
|
}
|
|
@@ -582,7 +584,7 @@ export function createRulesetInstaller({
|
|
|
582
584
|
}
|
|
583
585
|
/**
|
|
584
586
|
* The `instructions` entries that resolve to SynaBun's owned file and that SynaBun has no record of adding: the
|
|
585
|
-
* user's own lines, however they are spelled (`~/...`, relative to
|
|
587
|
+
* user's own lines, however they are spelled (`~/...`, relative to opencode.json, a symlink to the file). One listing
|
|
586
588
|
* of the recorded string is SynaBun's; every other is theirs. A URL or a glob pattern is never one of them.
|
|
587
589
|
*/
|
|
588
590
|
function userEntries(row, config) {
|
|
@@ -592,13 +594,13 @@ export function createRulesetInstaller({
|
|
|
592
594
|
return list.filter((value, index) => index !== mine && entryIsOwnedFile(value, config.path));
|
|
593
595
|
}
|
|
594
596
|
/**
|
|
595
|
-
* One change to
|
|
597
|
+
* One change to opencode.json. `plan(config)` answers null (nothing to do) or
|
|
596
598
|
* {text, done?}; `text: null` deletes the file. The file is replaced only
|
|
597
599
|
* while it still holds the bytes that were read: a change by another program
|
|
598
|
-
* in between is CHANGED_UNDERNEATH, and
|
|
600
|
+
* in between is CHANGED_UNDERNEATH, and opencode.json is left to it.
|
|
599
601
|
*/
|
|
600
|
-
function editOpencodeConfig(tally, plan) {
|
|
601
|
-
const config = readOpencodeConfig();
|
|
602
|
+
function editOpencodeConfig(tally, plan, path = layout().opencodeConfig) {
|
|
603
|
+
const config = readOpencodeConfig(path);
|
|
602
604
|
if (config.error) throw new Error(config.error);
|
|
603
605
|
const step = plan(config);
|
|
604
606
|
if (!step) return;
|
|
@@ -611,7 +613,7 @@ export function createRulesetInstaller({
|
|
|
611
613
|
tally.changed = true;
|
|
612
614
|
if (step.done) step.done();
|
|
613
615
|
}
|
|
614
|
-
/** Lists synabun.md in
|
|
616
|
+
/** Lists synabun.md in opencode.json unless it is listed already, and records the string SynaBun added. */
|
|
615
617
|
function addOpencodeEntry(row, tally) {
|
|
616
618
|
const value = opencodeEntry();
|
|
617
619
|
let listed = null;
|
|
@@ -645,7 +647,7 @@ export function createRulesetInstaller({
|
|
|
645
647
|
const mine = ownedEntry(row, config);
|
|
646
648
|
const others = theirs ? userEntries(row, config) : [];
|
|
647
649
|
if (mine === null && !others.length) return null;
|
|
648
|
-
//
|
|
650
|
+
// An opencode.json install created goes only while its bytes are still what install wrote. Anything the user
|
|
649
651
|
// added since makes it their file: the entries are edited out and the file stays.
|
|
650
652
|
if (mine !== null && row.created.includes(config.path) && config.text === newConfigText(mine)) return { text: null };
|
|
651
653
|
const dropKey = row.created.includes(`${config.path}#instructions`);
|
|
@@ -661,6 +663,60 @@ export function createRulesetInstaller({
|
|
|
661
663
|
});
|
|
662
664
|
}
|
|
663
665
|
|
|
666
|
+
// ── OpenCode 1's config.json ───────────────────────────────────────────────
|
|
667
|
+
// An older SynaBun listed synabun.md in config.json, which OpenCode 2 never reads. A copy SynaBun manages whose
|
|
668
|
+
// recorded entry is there is moved, at the next boot (reconcile) or install: listed in opencode.json, then the one
|
|
669
|
+
// string SynaBun added is taken out of config.json (the file goes when SynaBun made it and it still holds only that
|
|
670
|
+
// string). A remove takes that string out too. A line the user wrote in config.json is never touched. At a boot, a
|
|
671
|
+
// copy config.json no longer lists (the user took the line out, or the file) is an opt-out, as it was with
|
|
672
|
+
// OpenCode 1: never put back in opencode.json. An install, which the user asked for, lists it whatever config.json says.
|
|
673
|
+
function opencode1ConfigPath() {
|
|
674
|
+
return join(layout().opencodeDir, 'config.json');
|
|
675
|
+
}
|
|
676
|
+
/** The entry an older SynaBun recorded adding to config.json, or null. */
|
|
677
|
+
function opencode1Record(row) {
|
|
678
|
+
const owned = row.instructionsEntry;
|
|
679
|
+
return owned && typeof owned.value === 'string' && samePath(owned.config, opencode1ConfigPath()) ? { config: owned.config, value: owned.value } : null;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Whether config.json still lists the copy (`legacy`: opencode1Record), by any spelling: the line SynaBun added or
|
|
683
|
+
* one of the user's. A config.json SynaBun cannot read as plain JSON cannot say it does not, so it counts as listing.
|
|
684
|
+
*/
|
|
685
|
+
function opencode1Listed(legacy) {
|
|
686
|
+
const config = readOpencodeConfig(legacy.config);
|
|
687
|
+
return !!config.error || config.hasEntry;
|
|
688
|
+
}
|
|
689
|
+
/** Takes the string of `legacy` (opencode1Record) out of config.json, and SynaBun's record of what it made there. */
|
|
690
|
+
function removeOpencode1Entry(row, tally, legacy) {
|
|
691
|
+
editOpencodeConfig(tally, (config) => {
|
|
692
|
+
if (!config.exists || !config.cfg) return null;
|
|
693
|
+
const list = [...(config.cfg.instructions || [])];
|
|
694
|
+
const at = list.lastIndexOf(legacy.value);
|
|
695
|
+
if (at < 0) return null;
|
|
696
|
+
if (row.created.includes(config.path) && config.text === newConfigText(legacy.value)) return { text: null };
|
|
697
|
+
const dropKey = row.created.includes(`${config.path}#instructions`);
|
|
698
|
+
list.splice(at, 1);
|
|
699
|
+
const cfg = { ...config.cfg, instructions: list };
|
|
700
|
+
if (!list.length && dropKey) delete cfg.instructions;
|
|
701
|
+
return { text: configText(config.path, cfg, () => removeFromJsonList(config.text, 'instructions', legacy.value, { dropEmptyKey: dropKey })) };
|
|
702
|
+
}, legacy.config);
|
|
703
|
+
row.created = row.created.filter((path) => !samePath(path, legacy.config) && path !== `${legacy.config}#instructions`);
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* A managed copy listed in config.json only: listed in opencode.json, and SynaBun's string taken out of
|
|
707
|
+
* config.json (a failure there leaves it with a warning: OpenCode 2 does not read that file). False when the row
|
|
708
|
+
* has no such record.
|
|
709
|
+
*/
|
|
710
|
+
function moveOpencode1Entry(row, tally) {
|
|
711
|
+
const legacy = opencode1Record(row);
|
|
712
|
+
if (!legacy) return false;
|
|
713
|
+
addOpencodeEntry(row, tally);
|
|
714
|
+
try { removeOpencode1Entry(row, tally, legacy); } catch (error) {
|
|
715
|
+
tally.warnings.push(`${legacy.config} still lists ${legacy.value} under "instructions" (OpenCode 2 does not read that file): ${error?.message || error}`);
|
|
716
|
+
}
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
|
|
664
720
|
// ── Writing ────────────────────────────────────────────────────────────────
|
|
665
721
|
/**
|
|
666
722
|
* The pasted copies in `text`, sorted by what install may do with them:
|
|
@@ -847,8 +903,7 @@ export function createRulesetInstaller({
|
|
|
847
903
|
if (first.block) out.installedVersion = first.block.version;
|
|
848
904
|
if (host === 'opencode') {
|
|
849
905
|
const config = readOpencodeConfig();
|
|
850
|
-
|
|
851
|
-
if (sideFiles.length) out.notes = [`OpenCode also reads ${sideFiles.join(' and ')} in ${primary.dir}; SynaBun writes config.json, the same file its MCP toggle uses.`];
|
|
906
|
+
if (existsSync(join(primary.dir, 'opencode.jsonc'))) out.notes = [`OpenCode also reads opencode.jsonc in ${primary.dir}; SynaBun writes opencode.json, the same file its MCP toggle uses.`];
|
|
852
907
|
if (config.error) return { ...out, state: 'error', error: config.error, detail: config.error };
|
|
853
908
|
if (first.status !== 'missing' && !config.hasEntry) {
|
|
854
909
|
unlisted = `${config.path} does not list it under "instructions", so OpenCode does not load it.`;
|
|
@@ -858,7 +913,7 @@ export function createRulesetInstaller({
|
|
|
858
913
|
return { ...out, state: 'not-installed', installedVersion: null, detail: `${primary.path} exists, but ${unlisted}` };
|
|
859
914
|
}
|
|
860
915
|
}
|
|
861
|
-
// A line in
|
|
916
|
+
// A line in opencode.json that SynaBun did not add points at the file: a remove keeps both unless it is forced.
|
|
862
917
|
if (first.status !== 'missing' && userEntries(row, config).length) out.entry = 'user';
|
|
863
918
|
}
|
|
864
919
|
if (first.status === 'missing' && RANK[worst.status] < RANK.error) return { ...out, installedVersion: null };
|
|
@@ -991,7 +1046,7 @@ export function createRulesetInstaller({
|
|
|
991
1046
|
stripLegacy(host, target.legacyFile, tally);
|
|
992
1047
|
}
|
|
993
1048
|
}
|
|
994
|
-
if (host === 'opencode') addOpencodeEntry(row, tally);
|
|
1049
|
+
if (host === 'opencode' && !moveOpencode1Entry(row, tally)) addOpencodeEntry(row, tally);
|
|
995
1050
|
|
|
996
1051
|
Object.assign(row, {
|
|
997
1052
|
managed: true,
|
|
@@ -1055,10 +1110,10 @@ export function createRulesetInstaller({
|
|
|
1055
1110
|
* first; damaged markers, a block stamped for another tool, a symlinked owned
|
|
1056
1111
|
* file and a file that cannot be read are refused either way.
|
|
1057
1112
|
*
|
|
1058
|
-
* OpenCode: a line in
|
|
1113
|
+
* OpenCode: a line in opencode.json that points at the owned file and that
|
|
1059
1114
|
* SynaBun did not add keeps the file too (`kept`, reason `listed-in-config`):
|
|
1060
1115
|
* nothing is ever left pointing at a file SynaBun deleted. With `force` that
|
|
1061
|
-
* line goes as well, after a backup of
|
|
1116
|
+
* line goes as well, after a backup of opencode.json, and then the file.
|
|
1062
1117
|
*
|
|
1063
1118
|
* Symlinks at a block carrier (AGENTS.md, GEMINI.md): the block is taken out
|
|
1064
1119
|
* of the file the link points at and the link stays, exactly as install wrote
|
|
@@ -1076,7 +1131,7 @@ export function createRulesetInstaller({
|
|
|
1076
1131
|
* True while a copy that keeps a removal open is still on disk: one SynaBun
|
|
1077
1132
|
* should have taken and could not. That is damaged markers, a file that
|
|
1078
1133
|
* cannot be read, an untouched copy of this host's rules (behind a symlink,
|
|
1079
|
-
* or held back by another refusal), and for OpenCode
|
|
1134
|
+
* or held back by another refusal), and for OpenCode an opencode.json that
|
|
1080
1135
|
* cannot be edited while SynaBun's entry is on record. A copy the user
|
|
1081
1136
|
* edited, a file without markers and a block stamped for another tool are
|
|
1082
1137
|
* not: the keep rule leaves the first two to the user, and the third was
|
|
@@ -1169,6 +1224,11 @@ export function createRulesetInstaller({
|
|
|
1169
1224
|
if (go && doomed.length) recordIntent(doomed.map((entry) => entry.path));
|
|
1170
1225
|
if (go && together) {
|
|
1171
1226
|
try { removeOpencodeEntry(row, tally, { theirs: force && doomed.length > 0 }); } catch (error) { refuse(layout().opencodeConfig, 'error', error?.message || String(error)); }
|
|
1227
|
+
// The string an older SynaBun added to OpenCode 1's config.json goes too (OpenCode 2 never read it).
|
|
1228
|
+
const legacy = opencode1Record(row);
|
|
1229
|
+
if (legacy) {
|
|
1230
|
+
try { removeOpencode1Entry(row, tally, legacy); } catch (error) { tally.warnings.push(`${legacy.config} still lists ${legacy.value} under "instructions": ${error?.message || error}`); }
|
|
1231
|
+
}
|
|
1172
1232
|
}
|
|
1173
1233
|
if (!(together && refused.length)) {
|
|
1174
1234
|
for (const { path, text } of go ? doomed : []) {
|
|
@@ -1253,7 +1313,7 @@ export function createRulesetInstaller({
|
|
|
1253
1313
|
// (`created`, OpenCode's entry) stays on record, so a forced remove or a Replace can still clean up. The same
|
|
1254
1314
|
// row for a refusal that leaves nothing to wait for (a block stamped for another tool, a copy that turned out
|
|
1255
1315
|
// edited): the host is opted out, and no removal is kept open over a file that was never SynaBun's to take.
|
|
1256
|
-
// `paths` are places a copy of the rules is:
|
|
1316
|
+
// `paths` are places a copy of the rules is: opencode.json, which a refusal can name, is not one of them.
|
|
1257
1317
|
const config = host === 'opencode' ? layout().opencodeConfig : null;
|
|
1258
1318
|
Object.assign(row, {
|
|
1259
1319
|
managed: false, installedVersion: null, installedSha: null, path: primary.path,
|
|
@@ -1349,7 +1409,11 @@ export function createRulesetInstaller({
|
|
|
1349
1409
|
const config = host === 'opencode' ? readOpencodeConfig() : null;
|
|
1350
1410
|
if (config?.error) { states[host] = 'error'; continue; }
|
|
1351
1411
|
const first = isDir(primary.dir) ? inspect(host, primary, expected) : { status: 'missing' };
|
|
1352
|
-
|
|
1412
|
+
// Listed by an older SynaBun in OpenCode 1's config.json only: moved to opencode.json, never an opt-out. A copy
|
|
1413
|
+
// config.json does not list any more is not moved: the user took it out, which is the opt-out below.
|
|
1414
|
+
const older = config && first.status !== 'missing' && !config.hasEntry ? opencode1Record(row) : null;
|
|
1415
|
+
const moving = older !== null && opencode1Listed(older);
|
|
1416
|
+
if (first.status === 'missing' || (config && !config.hasEntry && !moving)) {
|
|
1353
1417
|
// The user deleted it: an opt-out, never a reason to put it back. (A copy SynaBun's own removal took is
|
|
1354
1418
|
// not this case: that removal is on record and was dealt with above.)
|
|
1355
1419
|
Object.assign(row, { managed: false, optedOutAt: stamp() });
|
|
@@ -1358,6 +1422,7 @@ export function createRulesetInstaller({
|
|
|
1358
1422
|
continue;
|
|
1359
1423
|
}
|
|
1360
1424
|
const tally = call.tally(host);
|
|
1425
|
+
if (moving) moveOpencode1Entry(row, tally);
|
|
1361
1426
|
const known = new Set(row.paths.map((path) => realKey(path)));
|
|
1362
1427
|
let primaryCurrent = first.status === 'current';
|
|
1363
1428
|
for (const target of list) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SynaBun — one entry of a JSON list, edited as text (pure, no fs)
|
|
3
3
|
// ═══════════════════════════════════════════
|
|
4
4
|
//
|
|
5
|
-
// OpenCode's
|
|
5
|
+
// OpenCode's opencode.json belongs to the user. Parsing it and writing it back
|
|
6
6
|
// would re-indent it, re-space it and drop whatever JSON.stringify spells
|
|
7
7
|
// differently, all to add one string to one list. These two functions change
|
|
8
8
|
// only the characters of that entry (and of the key, when SynaBun adds or takes
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from './db.js';
|
|
25
25
|
import { parseLine } from './session-chunker.js';
|
|
26
26
|
import { listCodexSessionsFromAccounts } from './codex-session-catalog.js';
|
|
27
|
-
import {
|
|
27
|
+
import { OPENCODE_SESSION_TABLES, hasOpencodeSessionTables, v2MessageText } from './opencode-db-schema.js';
|
|
28
28
|
|
|
29
29
|
const MAX_BODY = 256 * 1024;
|
|
30
30
|
const MAX_MSG_CHARS = 8 * 1024;
|
|
@@ -381,18 +381,15 @@ export function rebuildCodexCache({ project, projects, accounts, onProgress } =
|
|
|
381
381
|
|
|
382
382
|
// --- OpenCode ---
|
|
383
383
|
|
|
384
|
-
//
|
|
385
|
-
//
|
|
386
|
-
|
|
387
|
-
export function rebuildOpencodeCache({ getOpencodeDb, onProgress, protocol = null } = {}) {
|
|
384
|
+
// OpenCode 2 keeps its sessions in session_v2 and their text in session_message rows
|
|
385
|
+
// (lib/opencode-db-schema.js); a file without those tables has nothing to index.
|
|
386
|
+
export function rebuildOpencodeCache({ getOpencodeDb, onProgress } = {}) {
|
|
388
387
|
const emit = onProgress || (() => {});
|
|
389
388
|
if (typeof getOpencodeDb !== 'function') return { sessions: 0, updated: 0 };
|
|
390
389
|
|
|
391
390
|
const ocdb = getOpencodeDb();
|
|
392
|
-
if (!ocdb) return { sessions: 0, updated: 0 };
|
|
393
|
-
const
|
|
394
|
-
if (!flavor) return { sessions: 0, updated: 0 };
|
|
395
|
-
const tables = sessionTablesFor(flavor);
|
|
391
|
+
if (!ocdb || !hasOpencodeSessionTables(ocdb)) return { sessions: 0, updated: 0 };
|
|
392
|
+
const tables = OPENCODE_SESSION_TABLES;
|
|
396
393
|
|
|
397
394
|
let sessions;
|
|
398
395
|
try {
|
|
@@ -409,29 +406,19 @@ export function rebuildOpencodeCache({ getOpencodeDb, onProgress, protocol = nul
|
|
|
409
406
|
let total = sessions.length;
|
|
410
407
|
let updated = 0;
|
|
411
408
|
|
|
412
|
-
//
|
|
413
|
-
//
|
|
414
|
-
// OpenCode 2: one session_message row per message, its text in `data`
|
|
415
|
-
// (v2MessageText); read as the same { part_id, part_created, pdata, mdata } rows.
|
|
409
|
+
// One session_message row per message, its text in `data` (v2MessageText); read as
|
|
410
|
+
// { part_id, part_created, pdata, mdata } rows.
|
|
416
411
|
let partStmt;
|
|
417
412
|
try {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}).filter(Boolean),
|
|
428
|
-
};
|
|
429
|
-
})()
|
|
430
|
-
: ocdb.prepare(`SELECT p.id AS part_id, p.time_created AS part_created, p.data AS pdata, m.data AS mdata
|
|
431
|
-
FROM part p
|
|
432
|
-
LEFT JOIN message m ON m.id = p.message_id
|
|
433
|
-
WHERE p.session_id = ?
|
|
434
|
-
ORDER BY p.time_created ASC`);
|
|
413
|
+
const rows = ocdb.prepare(`SELECT id, type, time_created, data FROM session_message
|
|
414
|
+
WHERE session_id = ? AND type IN ('user', 'assistant')
|
|
415
|
+
ORDER BY seq ASC`);
|
|
416
|
+
partStmt = {
|
|
417
|
+
all: (sessionId) => rows.all(sessionId).map((row) => {
|
|
418
|
+
const said = v2MessageText(row.type, row.data);
|
|
419
|
+
return said ? { part_id: row.id, part_created: row.time_created, pdata: JSON.stringify({ type: 'text', text: said.text }), mdata: JSON.stringify({ role: said.role }) } : null;
|
|
420
|
+
}).filter(Boolean),
|
|
421
|
+
};
|
|
435
422
|
} catch { partStmt = null; }
|
|
436
423
|
|
|
437
424
|
for (const row of sessions) {
|
|
@@ -111,10 +111,32 @@ async function generateClaudeTitle(input, deps) {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Why a `codex exec` title run failed, in one short line: never its command
|
|
116
|
+
* line (the arguments name the project folder) and never its whole stderr.
|
|
117
|
+
*/
|
|
118
|
+
export function codexTitleFailure(error) {
|
|
119
|
+
const lines = String(error?.stderr || '').split(/\r?\n/).map(line => line.trim())
|
|
120
|
+
.filter(line => line && !/^Reading (?:prompt|additional input) from stdin/i.test(line));
|
|
121
|
+
const said = lines.length ? `: ${lines.at(-1).slice(0, 200)}` : '';
|
|
122
|
+
if (error?.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') return new Error('codex exec wrote more than its title run may');
|
|
123
|
+
if (error?.killed || error?.signal === 'SIGTERM') return new Error(`codex exec did not answer within ${SESSION_TITLE_TIMEOUT_MS / 1000} s${said}`);
|
|
124
|
+
if (error?.code === 'ENOENT') return new Error('codex was not found');
|
|
125
|
+
const status = Number.isInteger(error?.code) ? `exit code ${error.code}` : error?.code || error?.signal || 'an error';
|
|
126
|
+
return new Error(`codex exec failed (${status})${said}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
114
129
|
async function generateCodexTitle(input, deps) {
|
|
115
130
|
if (typeof deps.execFile !== 'function' || typeof deps.getCodexBin !== 'function') {
|
|
116
131
|
throw new Error('Codex title runner is unavailable');
|
|
117
132
|
}
|
|
133
|
+
// The prompt goes in on stdin, which is then ended, as the Codex SDK does,
|
|
134
|
+
// never as an argument. `codex exec` reads stdin whenever it is not a
|
|
135
|
+
// terminal: given a prompt argument it waits for stdin to end to add it to
|
|
136
|
+
// the prompt ("Reading additional input from stdin..."), so an open pipe held
|
|
137
|
+
// every title request until its timeout, whose error message then carried
|
|
138
|
+
// the whole command line (the person's prompt) into the server log. Windows
|
|
139
|
+
// also limits a command line to 32,767 characters.
|
|
118
140
|
const args = [
|
|
119
141
|
'exec', '--ephemeral', '--skip-git-repo-check', '--ignore-rules',
|
|
120
142
|
'--sandbox', 'read-only', '--color', 'never', '--json',
|
|
@@ -125,20 +147,39 @@ async function generateCodexTitle(input, deps) {
|
|
|
125
147
|
if (input.model) args.push('-m', input.model);
|
|
126
148
|
const effort = codexReasoningEffort(input.effort);
|
|
127
149
|
if (effort) args.push('-c', `model_reasoning_effort="${effort}"`);
|
|
128
|
-
args.push(titlePrompt(input));
|
|
129
150
|
let env = {
|
|
130
151
|
...(deps.env || process.env),
|
|
131
152
|
CODEX_HOME: deps.getCodexAccountHome?.(input.accountId || 'default'),
|
|
132
153
|
};
|
|
133
154
|
if (typeof deps.getAugmentedPath === 'function') env = withPathEnv(env, deps.getAugmentedPath());
|
|
134
|
-
|
|
155
|
+
// util.promisify(execFile) hands the child over as `.child`.
|
|
156
|
+
const running = deps.execFile(deps.getCodexBin(), args, {
|
|
135
157
|
cwd: input.cwd || process.cwd(),
|
|
136
158
|
env,
|
|
137
159
|
timeout: SESSION_TITLE_TIMEOUT_MS,
|
|
138
160
|
maxBuffer: 2 * 1024 * 1024,
|
|
139
161
|
windowsHide: true,
|
|
140
162
|
});
|
|
141
|
-
|
|
163
|
+
// Handled before anything else is done with the child: the run rejects when codex fails, is stopped or times out,
|
|
164
|
+
// and a rejection nobody handles ends the server (server.js handleProcessFailure).
|
|
165
|
+
const settled = Promise.resolve(running).then(result => ({ result }), error => ({ error }));
|
|
166
|
+
try {
|
|
167
|
+
const stdin = running?.child?.stdin;
|
|
168
|
+
if (stdin) {
|
|
169
|
+
// A Codex that ended before reading it: its exit says why, not this pipe.
|
|
170
|
+
stdin.on?.('error', () => {});
|
|
171
|
+
stdin.end(titlePrompt(input));
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
// The prompt could not be handed over: the run is stopped (it would wait for its stdin until the timeout) and
|
|
175
|
+
// its end awaited, never left behind.
|
|
176
|
+
try { running?.child?.kill?.(); } catch {}
|
|
177
|
+
await settled;
|
|
178
|
+
throw new Error(`codex exec could not be given the prompt (${error?.code || 'stdin'})`);
|
|
179
|
+
}
|
|
180
|
+
const { result, error } = await settled;
|
|
181
|
+
if (error) throw codexTitleFailure(error);
|
|
182
|
+
return extractCodexText(result?.stdout);
|
|
142
183
|
}
|
|
143
184
|
|
|
144
185
|
async function generateOpenCodeTitle(input, deps) {
|
|
@@ -152,42 +193,24 @@ async function generateOpenCodeTitle(input, deps) {
|
|
|
152
193
|
const timer = setTimeout(() => controller.abort(new Error('Session title request timed out')), SESSION_TITLE_TIMEOUT_MS);
|
|
153
194
|
timer.unref?.();
|
|
154
195
|
let sessionId = null;
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const text = String(generated?.data?.text || '').trim();
|
|
169
|
-
if (!generated?.error && text) { clearTimeout(timer); return text; }
|
|
170
|
-
} catch { /* the session below */ }
|
|
171
|
-
}
|
|
196
|
+
// The client's one-shot generator when the model allows it (OpenCode's free
|
|
197
|
+
// tier answers it 503), else a temporary session whose rules deny every tool:
|
|
198
|
+
// OpenCode has no tool switch per prompt.
|
|
199
|
+
try {
|
|
200
|
+
const generated = await deps.openCodeClient.getClient?.()?.generate?.({
|
|
201
|
+
prompt: `${TITLE_SYSTEM_PROMPT}\n\n${titlePrompt(input)}`,
|
|
202
|
+
model: input.model || undefined,
|
|
203
|
+
directory: input.cwd || undefined,
|
|
204
|
+
signal: controller.signal,
|
|
205
|
+
});
|
|
206
|
+
const text = String(generated?.data?.text || '').trim();
|
|
207
|
+
if (!generated?.error && text) { clearTimeout(timer); return text; }
|
|
208
|
+
} catch { /* the session below */ }
|
|
172
209
|
try {
|
|
173
|
-
const toolIds = new Set([
|
|
174
|
-
'bash', 'edit', 'write', 'patch', 'apply_patch', 'multiedit', 'read',
|
|
175
|
-
'glob', 'grep', 'list', 'task', 'webfetch', 'question', 'skill',
|
|
176
|
-
'todowrite', 'todoread',
|
|
177
|
-
]);
|
|
178
|
-
try {
|
|
179
|
-
const toolResult = await deps.openCodeClient.getClient?.()?.tool?.ids(
|
|
180
|
-
{ directory: input.cwd || undefined },
|
|
181
|
-
{ signal: controller.signal },
|
|
182
|
-
);
|
|
183
|
-
for (const id of (toolResult?.data || [])) if (id) toolIds.add(String(id));
|
|
184
|
-
} catch {}
|
|
185
|
-
const disabledTools = Object.fromEntries([...toolIds].map((id) => [id, false]));
|
|
186
|
-
|
|
187
210
|
const created = await deps.openCodeClient.session.create({
|
|
188
211
|
directory: input.cwd || undefined,
|
|
189
212
|
title: 'SessionTitleDraft',
|
|
190
|
-
|
|
213
|
+
permission: [{ action: '*', resource: '*', effect: 'deny' }],
|
|
191
214
|
});
|
|
192
215
|
sessionId = created?.data?.id || created?.data?.sessionID || created?.id || created?.sessionID;
|
|
193
216
|
if (!sessionId) throw new Error('OpenCode did not create a temporary title session');
|
|
@@ -199,7 +222,6 @@ async function generateOpenCodeTitle(input, deps) {
|
|
|
199
222
|
variant: input.variant || undefined,
|
|
200
223
|
system: TITLE_SYSTEM_PROMPT,
|
|
201
224
|
directory: input.cwd || undefined,
|
|
202
|
-
tools: disabledTools,
|
|
203
225
|
signal: controller.signal,
|
|
204
226
|
});
|
|
205
227
|
return extractOpenCodeText(response);
|