synabun 1.3.0 → 2.0.0-beta
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 +197 -662
- package/LICENSE-COMMERCIAL.md +33 -0
- package/NOTICE +17 -0
- package/README.md +175 -853
- package/THIRD-PARTY-LICENSES.md +131 -0
- package/benchmarks/browser-live-results.json +79 -0
- package/benchmarks/browser-v2-results.json +258 -0
- package/benchmarks/fixtures/browser/README.md +57 -0
- package/benchmarks/fixtures/browser/page-state.json +132 -0
- package/benchmarks/fixtures/browser/social.json +92 -0
- package/benchmarks/fixtures/browser/target.json +319 -0
- package/benchmarks/fixtures/desktop/README.md +58 -0
- package/benchmarks/fixtures/desktop/abstention.json +59 -0
- package/benchmarks/fixtures/desktop/adversarial.json +22 -0
- package/benchmarks/fixtures/desktop/forbidden.json +93 -0
- package/benchmarks/fixtures/desktop/generate.mjs +1023 -0
- package/benchmarks/fixtures/desktop/target.json +136 -0
- package/bin/synabun.js +30 -0
- package/docs/browser-v2.md +109 -0
- package/docs/hook-integrity.md +89 -0
- package/docs/judgments.md +182 -0
- package/docs/style-guide.md +246 -0
- package/docs/whatsapp.md +325 -0
- package/hooks/claude-code/post-plan.mjs +613 -391
- package/hooks/claude-code/post-remember.mjs +246 -122
- package/hooks/claude-code/pre-compact.mjs +171 -110
- package/hooks/claude-code/pre-task.mjs +133 -0
- package/hooks/claude-code/pre-websearch.mjs +15 -4
- package/hooks/claude-code/prompt-origin.mjs +94 -0
- package/hooks/claude-code/prompt-submit.mjs +1244 -861
- package/hooks/claude-code/session-start.mjs +114 -156
- package/hooks/claude-code/shared.mjs +1228 -368
- package/hooks/claude-code/state.mjs +167 -0
- package/hooks/claude-code/stop.mjs +515 -140
- package/hooks/claude-code/subagent-start.mjs +154 -0
- package/hooks/claude-code/subagent-stop.mjs +102 -0
- package/hooks/claude-code/task-gate.mjs +150 -0
- package/lib/claude-hooks.js +84 -0
- package/lib/claude-usage-audit.js +141 -0
- package/lib/client-config-repair.js +144 -0
- package/lib/data-home-migration.js +498 -0
- package/lib/first-launch-protection.js +240 -0
- package/lib/paths.d.ts +21 -0
- package/lib/paths.js +103 -0
- package/mcp-server/dist/config.d.ts +22 -0
- package/mcp-server/dist/config.d.ts.map +1 -0
- package/mcp-server/dist/config.js +109 -0
- package/mcp-server/dist/config.js.map +1 -0
- package/mcp-server/dist/http.d.ts +30 -0
- package/mcp-server/dist/http.d.ts.map +1 -0
- package/mcp-server/dist/http.js +276 -0
- package/mcp-server/dist/http.js.map +1 -0
- package/mcp-server/dist/index.d.ts +48 -0
- package/mcp-server/dist/index.d.ts.map +1 -0
- package/mcp-server/dist/index.js +538 -0
- package/mcp-server/dist/index.js.map +1 -0
- package/mcp-server/dist/preload-http.d.ts +3 -0
- package/mcp-server/dist/preload-http.d.ts.map +1 -0
- package/mcp-server/dist/preload-http.js +28 -0
- package/mcp-server/dist/preload-http.js.map +1 -0
- package/mcp-server/dist/preload.d.ts +3 -0
- package/mcp-server/dist/preload.d.ts.map +1 -0
- package/mcp-server/dist/preload.js +32 -0
- package/mcp-server/dist/preload.js.map +1 -0
- package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.d.ts +19 -0
- package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.d.ts.map +1 -0
- package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.js +403 -0
- package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.js.map +1 -0
- package/mcp-server/dist/scripts/setup-embeddings.d.ts +3 -0
- package/mcp-server/dist/scripts/setup-embeddings.d.ts.map +1 -0
- package/mcp-server/dist/scripts/setup-embeddings.js +15 -0
- package/mcp-server/dist/scripts/setup-embeddings.js.map +1 -0
- package/mcp-server/dist/services/assistant-judgments.d.ts +79 -0
- package/mcp-server/dist/services/assistant-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/assistant-judgments.js +128 -0
- package/mcp-server/dist/services/assistant-judgments.js.map +1 -0
- package/mcp-server/dist/services/browser-assist-gate.d.ts +314 -0
- package/mcp-server/dist/services/browser-assist-gate.d.ts.map +1 -0
- package/mcp-server/dist/services/browser-assist-gate.js +344 -0
- package/mcp-server/dist/services/browser-assist-gate.js.map +1 -0
- package/mcp-server/dist/services/browser-assist.d.ts +104 -0
- package/mcp-server/dist/services/browser-assist.d.ts.map +1 -0
- package/mcp-server/dist/services/browser-assist.js +282 -0
- package/mcp-server/dist/services/browser-assist.js.map +1 -0
- package/mcp-server/dist/services/browser-bench.d.ts +772 -0
- package/mcp-server/dist/services/browser-bench.d.ts.map +1 -0
- package/mcp-server/dist/services/browser-bench.js +424 -0
- package/mcp-server/dist/services/browser-bench.js.map +1 -0
- package/mcp-server/dist/services/browser-judgments.d.ts +196 -0
- package/mcp-server/dist/services/browser-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/browser-judgments.js +363 -0
- package/mcp-server/dist/services/browser-judgments.js.map +1 -0
- package/mcp-server/dist/services/browser-risk.d.ts +166 -0
- package/mcp-server/dist/services/browser-risk.d.ts.map +1 -0
- package/mcp-server/dist/services/browser-risk.js +458 -0
- package/mcp-server/dist/services/browser-risk.js.map +1 -0
- package/mcp-server/dist/services/categories.d.ts +61 -0
- package/mcp-server/dist/services/categories.d.ts.map +1 -0
- package/mcp-server/dist/services/categories.js +377 -0
- package/mcp-server/dist/services/categories.js.map +1 -0
- package/mcp-server/dist/services/codex-config-heal.d.ts +49 -0
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -0
- package/mcp-server/dist/services/codex-config-heal.js +346 -0
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -0
- package/mcp-server/dist/services/desktop-assist-gate.d.ts +198 -0
- package/mcp-server/dist/services/desktop-assist-gate.d.ts.map +1 -0
- package/mcp-server/dist/services/desktop-assist-gate.js +202 -0
- package/mcp-server/dist/services/desktop-assist-gate.js.map +1 -0
- package/mcp-server/dist/services/desktop-assist.d.ts +104 -0
- package/mcp-server/dist/services/desktop-assist.d.ts.map +1 -0
- package/mcp-server/dist/services/desktop-assist.js +266 -0
- package/mcp-server/dist/services/desktop-assist.js.map +1 -0
- package/mcp-server/dist/services/desktop-bench.d.ts +553 -0
- package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -0
- package/mcp-server/dist/services/desktop-bench.js +311 -0
- package/mcp-server/dist/services/desktop-bench.js.map +1 -0
- package/mcp-server/dist/services/desktop-client.d.ts +23 -0
- package/mcp-server/dist/services/desktop-client.d.ts.map +1 -0
- package/mcp-server/dist/services/desktop-client.js +79 -0
- package/mcp-server/dist/services/desktop-client.js.map +1 -0
- package/mcp-server/dist/services/desktop-judgments.d.ts +125 -0
- package/mcp-server/dist/services/desktop-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/desktop-judgments.js +177 -0
- package/mcp-server/dist/services/desktop-judgments.js.map +1 -0
- package/mcp-server/dist/services/desktop-risk.d.ts +283 -0
- package/mcp-server/dist/services/desktop-risk.d.ts.map +1 -0
- package/mcp-server/dist/services/desktop-risk.js +605 -0
- package/mcp-server/dist/services/desktop-risk.js.map +1 -0
- package/mcp-server/dist/services/discord.d.ts +112 -0
- package/mcp-server/dist/services/discord.d.ts.map +1 -0
- package/mcp-server/dist/services/discord.js +477 -0
- package/mcp-server/dist/services/discord.js.map +1 -0
- package/mcp-server/dist/services/edit-stale.d.ts +81 -0
- package/mcp-server/dist/services/edit-stale.d.ts.map +1 -0
- package/mcp-server/dist/services/edit-stale.js +358 -0
- package/mcp-server/dist/services/edit-stale.js.map +1 -0
- package/mcp-server/dist/services/embedding-service.d.ts +16 -0
- package/mcp-server/dist/services/embedding-service.d.ts.map +1 -0
- package/mcp-server/dist/services/embedding-service.js +126 -0
- package/mcp-server/dist/services/embedding-service.js.map +1 -0
- package/mcp-server/dist/services/embedding-worker.d.ts +2 -0
- package/mcp-server/dist/services/embedding-worker.d.ts.map +1 -0
- package/mcp-server/dist/services/embedding-worker.js +52 -0
- package/mcp-server/dist/services/embedding-worker.js.map +1 -0
- package/mcp-server/dist/services/fb-regions.d.ts +71 -0
- package/mcp-server/dist/services/fb-regions.d.ts.map +1 -0
- package/mcp-server/dist/services/fb-regions.js +211 -0
- package/mcp-server/dist/services/fb-regions.js.map +1 -0
- package/mcp-server/dist/services/file-checksums.d.ts +22 -0
- package/mcp-server/dist/services/file-checksums.d.ts.map +1 -0
- package/mcp-server/dist/services/file-checksums.js +55 -0
- package/mcp-server/dist/services/file-checksums.js.map +1 -0
- package/mcp-server/dist/services/file-excerpt.d.ts +24 -0
- package/mcp-server/dist/services/file-excerpt.d.ts.map +1 -0
- package/mcp-server/dist/services/file-excerpt.js +65 -0
- package/mcp-server/dist/services/file-excerpt.js.map +1 -0
- package/mcp-server/dist/services/identity.d.ts +124 -0
- package/mcp-server/dist/services/identity.d.ts.map +1 -0
- package/mcp-server/dist/services/identity.js +241 -0
- package/mcp-server/dist/services/identity.js.map +1 -0
- package/mcp-server/dist/services/local-embeddings.d.ts +2 -0
- package/mcp-server/dist/services/local-embeddings.d.ts.map +1 -0
- package/mcp-server/dist/services/local-embeddings.js +3 -0
- package/mcp-server/dist/services/local-embeddings.js.map +1 -0
- package/mcp-server/dist/services/memory-client.d.ts +4 -0
- package/mcp-server/dist/services/memory-client.d.ts.map +1 -0
- package/mcp-server/dist/services/memory-client.js +27 -0
- package/mcp-server/dist/services/memory-client.js.map +1 -0
- package/mcp-server/dist/services/memory-judge-backfill.d.ts +135 -0
- package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -0
- package/mcp-server/dist/services/memory-judge-backfill.js +510 -0
- package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -0
- package/mcp-server/dist/services/memory-judgments.d.ts +362 -0
- package/mcp-server/dist/services/memory-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/memory-judgments.js +674 -0
- package/mcp-server/dist/services/memory-judgments.js.map +1 -0
- package/mcp-server/dist/services/memory-maintenance.d.ts +108 -0
- package/mcp-server/dist/services/memory-maintenance.d.ts.map +1 -0
- package/mcp-server/dist/services/memory-maintenance.js +403 -0
- package/mcp-server/dist/services/memory-maintenance.js.map +1 -0
- package/mcp-server/dist/services/memory-retrieval.d.ts +66 -0
- package/mcp-server/dist/services/memory-retrieval.d.ts.map +1 -0
- package/mcp-server/dist/services/memory-retrieval.js +412 -0
- package/mcp-server/dist/services/memory-retrieval.js.map +1 -0
- package/mcp-server/dist/services/memory-schema.d.ts +16 -0
- package/mcp-server/dist/services/memory-schema.d.ts.map +1 -0
- package/mcp-server/dist/services/memory-schema.js +282 -0
- package/mcp-server/dist/services/memory-schema.js.map +1 -0
- package/mcp-server/dist/services/memory-sessions.d.ts +24 -0
- package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -0
- package/mcp-server/dist/services/memory-sessions.js +110 -0
- package/mcp-server/dist/services/memory-sessions.js.map +1 -0
- package/mcp-server/dist/services/memory-triage.d.ts +60 -0
- package/mcp-server/dist/services/memory-triage.d.ts.map +1 -0
- package/mcp-server/dist/services/memory-triage.js +189 -0
- package/mcp-server/dist/services/memory-triage.js.map +1 -0
- package/mcp-server/dist/services/memory-verification.d.ts +24 -0
- package/mcp-server/dist/services/memory-verification.d.ts.map +1 -0
- package/mcp-server/dist/services/memory-verification.js +92 -0
- package/mcp-server/dist/services/memory-verification.js.map +1 -0
- package/mcp-server/dist/services/memory-writer.d.ts +26 -0
- package/mcp-server/dist/services/memory-writer.d.ts.map +1 -0
- package/mcp-server/dist/services/memory-writer.js +126 -0
- package/mcp-server/dist/services/memory-writer.js.map +1 -0
- package/mcp-server/dist/services/neural-interface.d.ts +439 -0
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -0
- package/mcp-server/dist/services/neural-interface.js +1135 -0
- package/mcp-server/dist/services/neural-interface.js.map +1 -0
- package/mcp-server/dist/services/profiles.d.ts +120 -0
- package/mcp-server/dist/services/profiles.d.ts.map +1 -0
- package/mcp-server/dist/services/profiles.js +387 -0
- package/mcp-server/dist/services/profiles.js.map +1 -0
- package/mcp-server/dist/services/secret-gate.d.ts +29 -0
- package/mcp-server/dist/services/secret-gate.d.ts.map +1 -0
- package/mcp-server/dist/services/secret-gate.js +43 -0
- package/mcp-server/dist/services/secret-gate.js.map +1 -0
- package/mcp-server/dist/services/server-instructions.d.ts +10 -0
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -0
- package/mcp-server/dist/services/server-instructions.js +101 -0
- package/mcp-server/dist/services/server-instructions.js.map +1 -0
- package/mcp-server/dist/services/sqlite.d.ts +235 -0
- package/mcp-server/dist/services/sqlite.d.ts.map +1 -0
- package/mcp-server/dist/services/sqlite.js +1185 -0
- package/mcp-server/dist/services/sqlite.js.map +1 -0
- package/mcp-server/dist/services/typesafe-bench.d.ts +99 -0
- package/mcp-server/dist/services/typesafe-bench.d.ts.map +1 -0
- package/mcp-server/dist/services/typesafe-bench.js +218 -0
- package/mcp-server/dist/services/typesafe-bench.js.map +1 -0
- package/mcp-server/dist/services/typesafe-config.d.ts +342 -0
- package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -0
- package/mcp-server/dist/services/typesafe-config.js +746 -0
- package/mcp-server/dist/services/typesafe-config.js.map +1 -0
- package/mcp-server/dist/services/typesafe.d.ts +213 -0
- package/mcp-server/dist/services/typesafe.d.ts.map +1 -0
- package/mcp-server/dist/services/typesafe.js +330 -0
- package/mcp-server/dist/services/typesafe.js.map +1 -0
- package/mcp-server/dist/services/vector-cache.d.ts +68 -0
- package/mcp-server/dist/services/vector-cache.d.ts.map +1 -0
- package/mcp-server/dist/services/vector-cache.js +271 -0
- package/mcp-server/dist/services/vector-cache.js.map +1 -0
- package/mcp-server/dist/services/whiteboard-geometry.d.ts +305 -0
- package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -0
- package/mcp-server/dist/services/whiteboard-geometry.js +666 -0
- package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -0
- package/mcp-server/dist/tools/agents.d.ts +212 -0
- package/mcp-server/dist/tools/agents.d.ts.map +1 -0
- package/mcp-server/dist/tools/agents.js +407 -0
- package/mcp-server/dist/tools/agents.js.map +1 -0
- package/mcp-server/dist/tools/bluesky.d.ts +39 -0
- package/mcp-server/dist/tools/bluesky.d.ts.map +1 -0
- package/mcp-server/dist/tools/bluesky.js +1144 -0
- package/mcp-server/dist/tools/bluesky.js.map +1 -0
- package/mcp-server/dist/tools/browser-advanced.d.ts +58 -0
- package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -0
- package/mcp-server/dist/tools/browser-advanced.js +106 -0
- package/mcp-server/dist/tools/browser-advanced.js.map +1 -0
- package/mcp-server/dist/tools/browser-batch.d.ts +99 -0
- package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -0
- package/mcp-server/dist/tools/browser-batch.js +187 -0
- package/mcp-server/dist/tools/browser-batch.js.map +1 -0
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts +21 -0
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -0
- package/mcp-server/dist/tools/browser-cheatsheet.js +144 -0
- package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -0
- package/mcp-server/dist/tools/browser-interact.d.ts +239 -0
- package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -0
- package/mcp-server/dist/tools/browser-interact.js +300 -0
- package/mcp-server/dist/tools/browser-interact.js.map +1 -0
- package/mcp-server/dist/tools/browser-navigate.d.ts +90 -0
- package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -0
- package/mcp-server/dist/tools/browser-navigate.js +142 -0
- package/mcp-server/dist/tools/browser-navigate.js.map +1 -0
- package/mcp-server/dist/tools/browser-observe.d.ts +561 -0
- package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -0
- package/mcp-server/dist/tools/browser-observe.js +2009 -0
- package/mcp-server/dist/tools/browser-observe.js.map +1 -0
- package/mcp-server/dist/tools/browser.d.ts +34 -0
- package/mcp-server/dist/tools/browser.d.ts.map +1 -0
- package/mcp-server/dist/tools/browser.js +112 -0
- package/mcp-server/dist/tools/browser.js.map +1 -0
- package/mcp-server/dist/tools/card-tools.d.ts +82 -0
- package/mcp-server/dist/tools/card-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/card-tools.js +137 -0
- package/mcp-server/dist/tools/card-tools.js.map +1 -0
- package/mcp-server/dist/tools/card.d.ts +8 -0
- package/mcp-server/dist/tools/card.d.ts.map +1 -0
- package/mcp-server/dist/tools/card.js +16 -0
- package/mcp-server/dist/tools/card.js.map +1 -0
- package/mcp-server/dist/tools/category.d.ts +32 -0
- package/mcp-server/dist/tools/category.d.ts.map +1 -0
- package/mcp-server/dist/tools/category.js +290 -0
- package/mcp-server/dist/tools/category.js.map +1 -0
- package/mcp-server/dist/tools/choice.d.ts +160 -0
- package/mcp-server/dist/tools/choice.d.ts.map +1 -0
- package/mcp-server/dist/tools/choice.js +80 -0
- package/mcp-server/dist/tools/choice.js.map +1 -0
- package/mcp-server/dist/tools/computer.d.ts +71 -0
- package/mcp-server/dist/tools/computer.d.ts.map +1 -0
- package/mcp-server/dist/tools/computer.js +174 -0
- package/mcp-server/dist/tools/computer.js.map +1 -0
- package/mcp-server/dist/tools/discord-channel.d.ts +44 -0
- package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -0
- package/mcp-server/dist/tools/discord-channel.js +236 -0
- package/mcp-server/dist/tools/discord-channel.js.map +1 -0
- package/mcp-server/dist/tools/discord-guild.d.ts +20 -0
- package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -0
- package/mcp-server/dist/tools/discord-guild.js +138 -0
- package/mcp-server/dist/tools/discord-guild.js.map +1 -0
- package/mcp-server/dist/tools/discord-member.d.ts +26 -0
- package/mcp-server/dist/tools/discord-member.d.ts.map +1 -0
- package/mcp-server/dist/tools/discord-member.js +122 -0
- package/mcp-server/dist/tools/discord-member.js.map +1 -0
- package/mcp-server/dist/tools/discord-message.d.ts +36 -0
- package/mcp-server/dist/tools/discord-message.d.ts.map +1 -0
- package/mcp-server/dist/tools/discord-message.js +184 -0
- package/mcp-server/dist/tools/discord-message.js.map +1 -0
- package/mcp-server/dist/tools/discord-onboarding.d.ts +32 -0
- package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -0
- package/mcp-server/dist/tools/discord-onboarding.js +179 -0
- package/mcp-server/dist/tools/discord-onboarding.js.map +1 -0
- package/mcp-server/dist/tools/discord-role.d.ts +34 -0
- package/mcp-server/dist/tools/discord-role.d.ts.map +1 -0
- package/mcp-server/dist/tools/discord-role.js +178 -0
- package/mcp-server/dist/tools/discord-role.js.map +1 -0
- package/mcp-server/dist/tools/discord-thread.d.ts +28 -0
- package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -0
- package/mcp-server/dist/tools/discord-thread.js +88 -0
- package/mcp-server/dist/tools/discord-thread.js.map +1 -0
- package/mcp-server/dist/tools/discord-webhook.d.ts +32 -0
- package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -0
- package/mcp-server/dist/tools/discord-webhook.js +154 -0
- package/mcp-server/dist/tools/discord-webhook.js.map +1 -0
- package/mcp-server/dist/tools/discord.d.ts +7 -0
- package/mcp-server/dist/tools/discord.d.ts.map +1 -0
- package/mcp-server/dist/tools/discord.js +25 -0
- package/mcp-server/dist/tools/discord.js.map +1 -0
- package/mcp-server/dist/tools/fb-groups.d.ts +98 -0
- package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -0
- package/mcp-server/dist/tools/fb-groups.js +369 -0
- package/mcp-server/dist/tools/fb-groups.js.map +1 -0
- package/mcp-server/dist/tools/forget.d.ts +14 -0
- package/mcp-server/dist/tools/forget.d.ts.map +1 -0
- package/mcp-server/dist/tools/forget.js +26 -0
- package/mcp-server/dist/tools/forget.js.map +1 -0
- package/mcp-server/dist/tools/git-tools.d.ts +24 -0
- package/mcp-server/dist/tools/git-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/git-tools.js +107 -0
- package/mcp-server/dist/tools/git-tools.js.map +1 -0
- package/mcp-server/dist/tools/git.d.ts +7 -0
- package/mcp-server/dist/tools/git.d.ts.map +1 -0
- package/mcp-server/dist/tools/git.js +11 -0
- package/mcp-server/dist/tools/git.js.map +1 -0
- package/mcp-server/dist/tools/gsc/experience.d.ts +135 -0
- package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -0
- package/mcp-server/dist/tools/gsc/experience.js +351 -0
- package/mcp-server/dist/tools/gsc/experience.js.map +1 -0
- package/mcp-server/dist/tools/gsc/helpers.d.ts +78 -0
- package/mcp-server/dist/tools/gsc/helpers.d.ts.map +1 -0
- package/mcp-server/dist/tools/gsc/helpers.js +258 -0
- package/mcp-server/dist/tools/gsc/helpers.js.map +1 -0
- package/mcp-server/dist/tools/gsc/indexing.d.ts +121 -0
- package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -0
- package/mcp-server/dist/tools/gsc/indexing.js +357 -0
- package/mcp-server/dist/tools/gsc/indexing.js.map +1 -0
- package/mcp-server/dist/tools/gsc/inspect.d.ts +78 -0
- package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -0
- package/mcp-server/dist/tools/gsc/inspect.js +321 -0
- package/mcp-server/dist/tools/gsc/inspect.js.map +1 -0
- package/mcp-server/dist/tools/gsc/navigate.d.ts +44 -0
- package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -0
- package/mcp-server/dist/tools/gsc/navigate.js +197 -0
- package/mcp-server/dist/tools/gsc/navigate.js.map +1 -0
- package/mcp-server/dist/tools/gsc/performance.d.ts +84 -0
- package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -0
- package/mcp-server/dist/tools/gsc/performance.js +274 -0
- package/mcp-server/dist/tools/gsc/performance.js.map +1 -0
- package/mcp-server/dist/tools/gsc/settings.d.ts +153 -0
- package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -0
- package/mcp-server/dist/tools/gsc/settings.js +425 -0
- package/mcp-server/dist/tools/gsc/settings.js.map +1 -0
- package/mcp-server/dist/tools/gsc.d.ts +7 -0
- package/mcp-server/dist/tools/gsc.d.ts.map +1 -0
- package/mcp-server/dist/tools/gsc.js +51 -0
- package/mcp-server/dist/tools/gsc.js.map +1 -0
- package/mcp-server/dist/tools/image-tools.d.ts +24 -0
- package/mcp-server/dist/tools/image-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/image-tools.js +80 -0
- package/mcp-server/dist/tools/image-tools.js.map +1 -0
- package/mcp-server/dist/tools/image.d.ts +10 -0
- package/mcp-server/dist/tools/image.d.ts.map +1 -0
- package/mcp-server/dist/tools/image.js +14 -0
- package/mcp-server/dist/tools/image.js.map +1 -0
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +97 -0
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/leonardo-browser-tools.js +228 -0
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -0
- package/mcp-server/dist/tools/leonardo.d.ts +13 -0
- package/mcp-server/dist/tools/leonardo.d.ts.map +1 -0
- package/mcp-server/dist/tools/leonardo.js +21 -0
- package/mcp-server/dist/tools/leonardo.js.map +1 -0
- package/mcp-server/dist/tools/loop.d.ts +30 -0
- package/mcp-server/dist/tools/loop.d.ts.map +1 -0
- package/mcp-server/dist/tools/loop.js +401 -0
- package/mcp-server/dist/tools/loop.js.map +1 -0
- package/mcp-server/dist/tools/memories.d.ts +56 -0
- package/mcp-server/dist/tools/memories.d.ts.map +1 -0
- package/mcp-server/dist/tools/memories.js +171 -0
- package/mcp-server/dist/tools/memories.js.map +1 -0
- package/mcp-server/dist/tools/morelogin.d.ts +24 -0
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -0
- package/mcp-server/dist/tools/morelogin.js +79 -0
- package/mcp-server/dist/tools/morelogin.js.map +1 -0
- package/mcp-server/dist/tools/profile.d.ts +17 -0
- package/mcp-server/dist/tools/profile.d.ts.map +1 -0
- package/mcp-server/dist/tools/profile.js +114 -0
- package/mcp-server/dist/tools/profile.js.map +1 -0
- package/mcp-server/dist/tools/recall.d.ts +71 -0
- package/mcp-server/dist/tools/recall.d.ts.map +1 -0
- package/mcp-server/dist/tools/recall.js +147 -0
- package/mcp-server/dist/tools/recall.js.map +1 -0
- package/mcp-server/dist/tools/reflect.d.ts +56 -0
- package/mcp-server/dist/tools/reflect.d.ts.map +1 -0
- package/mcp-server/dist/tools/reflect.js +157 -0
- package/mcp-server/dist/tools/reflect.js.map +1 -0
- package/mcp-server/dist/tools/remember.d.ts +62 -0
- package/mcp-server/dist/tools/remember.d.ts.map +1 -0
- package/mcp-server/dist/tools/remember.js +201 -0
- package/mcp-server/dist/tools/remember.js.map +1 -0
- package/mcp-server/dist/tools/response.d.ts +23 -0
- package/mcp-server/dist/tools/response.d.ts.map +1 -0
- package/mcp-server/dist/tools/response.js +24 -0
- package/mcp-server/dist/tools/response.js.map +1 -0
- package/mcp-server/dist/tools/restore.d.ts +14 -0
- package/mcp-server/dist/tools/restore.d.ts.map +1 -0
- package/mcp-server/dist/tools/restore.js +23 -0
- package/mcp-server/dist/tools/restore.js.map +1 -0
- package/mcp-server/dist/tools/style-guide-tools.d.ts +51 -0
- package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/style-guide-tools.js +231 -0
- package/mcp-server/dist/tools/style-guide-tools.js.map +1 -0
- package/mcp-server/dist/tools/style-guide.d.ts +8 -0
- package/mcp-server/dist/tools/style-guide.d.ts.map +1 -0
- package/mcp-server/dist/tools/style-guide.js +12 -0
- package/mcp-server/dist/tools/style-guide.js.map +1 -0
- package/mcp-server/dist/tools/sync.d.ts +20 -0
- package/mcp-server/dist/tools/sync.d.ts.map +1 -0
- package/mcp-server/dist/tools/sync.js +156 -0
- package/mcp-server/dist/tools/sync.js.map +1 -0
- package/mcp-server/dist/tools/tictactoe-tools.d.ts +18 -0
- package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/tictactoe-tools.js +78 -0
- package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -0
- package/mcp-server/dist/tools/tictactoe.d.ts +7 -0
- package/mcp-server/dist/tools/tictactoe.d.ts.map +1 -0
- package/mcp-server/dist/tools/tictactoe.js +11 -0
- package/mcp-server/dist/tools/tictactoe.js.map +1 -0
- package/mcp-server/dist/tools/utils.d.ts +19 -0
- package/mcp-server/dist/tools/utils.d.ts.map +1 -0
- package/mcp-server/dist/tools/utils.js +54 -0
- package/mcp-server/dist/tools/utils.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-format.d.ts +88 -0
- package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -0
- package/mcp-server/dist/tools/whiteboard-format.js +337 -0
- package/mcp-server/dist/tools/whiteboard-format.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +326 -0
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/whiteboard-tools.js +222 -0
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard.d.ts +8 -0
- package/mcp-server/dist/tools/whiteboard.d.ts.map +1 -0
- package/mcp-server/dist/tools/whiteboard.js +16 -0
- package/mcp-server/dist/tools/whiteboard.js.map +1 -0
- package/mcp-server/dist/tools/youtube/analytics.d.ts +46 -0
- package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/analytics.js +83 -0
- package/mcp-server/dist/tools/youtube/analytics.js.map +1 -0
- package/mcp-server/dist/tools/youtube/dedup.d.ts +35 -0
- package/mcp-server/dist/tools/youtube/dedup.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/dedup.js +56 -0
- package/mcp-server/dist/tools/youtube/dedup.js.map +1 -0
- package/mcp-server/dist/tools/youtube/download.d.ts +31 -0
- package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/download.js +30 -0
- package/mcp-server/dist/tools/youtube/download.js.map +1 -0
- package/mcp-server/dist/tools/youtube/helpers.d.ts +66 -0
- package/mcp-server/dist/tools/youtube/helpers.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/helpers.js +143 -0
- package/mcp-server/dist/tools/youtube/helpers.js.map +1 -0
- package/mcp-server/dist/tools/youtube/media.d.ts +43 -0
- package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/media.js +97 -0
- package/mcp-server/dist/tools/youtube/media.js.map +1 -0
- package/mcp-server/dist/tools/youtube/metadata.d.ts +33 -0
- package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/metadata.js +106 -0
- package/mcp-server/dist/tools/youtube/metadata.js.map +1 -0
- package/mcp-server/dist/tools/youtube/navigate.d.ts +30 -0
- package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/navigate.js +71 -0
- package/mcp-server/dist/tools/youtube/navigate.js.map +1 -0
- package/mcp-server/dist/tools/youtube/playlist.d.ts +29 -0
- package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/playlist.js +128 -0
- package/mcp-server/dist/tools/youtube/playlist.js.map +1 -0
- package/mcp-server/dist/tools/youtube/schedule.d.ts +31 -0
- package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/schedule.js +147 -0
- package/mcp-server/dist/tools/youtube/schedule.js.map +1 -0
- package/mcp-server/dist/tools/youtube/source.d.ts +36 -0
- package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/source.js +85 -0
- package/mcp-server/dist/tools/youtube/source.js.map +1 -0
- package/mcp-server/dist/tools/youtube/upload.d.ts +53 -0
- package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube/upload.js +255 -0
- package/mcp-server/dist/tools/youtube/upload.js.map +1 -0
- package/mcp-server/dist/tools/youtube.d.ts +12 -0
- package/mcp-server/dist/tools/youtube.d.ts.map +1 -0
- package/mcp-server/dist/tools/youtube.js +38 -0
- package/mcp-server/dist/tools/youtube.js.map +1 -0
- package/mcp-server/dist/tui.d.ts +3 -0
- package/mcp-server/dist/tui.d.ts.map +1 -0
- package/mcp-server/dist/tui.js +806 -0
- package/mcp-server/dist/tui.js.map +1 -0
- package/mcp-server/dist/types.d.ts +77 -0
- package/mcp-server/dist/types.d.ts.map +1 -0
- package/mcp-server/dist/types.js +2 -0
- package/mcp-server/dist/types.js.map +1 -0
- package/mcp-server/package-lock.json +4798 -0
- package/mcp-server/package.json +55 -38
- package/mcp-server/run.mjs +12 -2
- package/mcp-server/src/config.ts +48 -3
- package/mcp-server/src/http.ts +296 -93
- package/mcp-server/src/index.ts +546 -203
- package/mcp-server/src/preload-http.ts +19 -18
- package/mcp-server/src/preload.ts +19 -18
- package/mcp-server/src/scripts/setup-embeddings.ts +9 -0
- package/mcp-server/src/services/assistant-judgments.ts +157 -0
- package/mcp-server/src/services/browser-assist-gate.ts +383 -0
- package/mcp-server/src/services/browser-assist.ts +267 -0
- package/mcp-server/src/services/browser-bench.ts +477 -0
- package/mcp-server/src/services/browser-judgments.ts +414 -0
- package/mcp-server/src/services/browser-risk.ts +484 -0
- package/mcp-server/src/services/categories.ts +36 -19
- package/mcp-server/src/services/codex-config-heal.ts +403 -0
- package/mcp-server/src/services/desktop-assist-gate.ts +251 -0
- package/mcp-server/src/services/desktop-assist.ts +308 -0
- package/mcp-server/src/services/desktop-bench.ts +372 -0
- package/mcp-server/src/services/desktop-client.ts +76 -0
- package/mcp-server/src/services/desktop-judgments.ts +218 -0
- package/mcp-server/src/services/desktop-risk.ts +743 -0
- package/mcp-server/src/services/edit-stale.ts +299 -0
- package/mcp-server/src/services/embedding-service.ts +108 -0
- package/mcp-server/src/services/embedding-worker.ts +46 -0
- package/mcp-server/src/services/fb-regions.ts +221 -0
- package/mcp-server/src/services/file-checksums.ts +26 -9
- package/mcp-server/src/services/file-excerpt.ts +59 -0
- package/mcp-server/src/services/identity.ts +291 -0
- package/mcp-server/src/services/local-embeddings.ts +2 -77
- package/mcp-server/src/services/memory-client.ts +23 -0
- package/mcp-server/src/services/memory-judge-backfill.ts +462 -0
- package/mcp-server/src/services/memory-judgments.ts +901 -0
- package/mcp-server/src/services/memory-maintenance.ts +360 -0
- package/mcp-server/src/services/memory-retrieval.ts +349 -0
- package/mcp-server/src/services/memory-schema.ts +280 -0
- package/mcp-server/src/services/memory-sessions.ts +87 -0
- package/mcp-server/src/services/memory-triage.ts +182 -0
- package/mcp-server/src/services/memory-verification.ts +68 -0
- package/mcp-server/src/services/memory-writer.ts +125 -0
- package/mcp-server/src/services/neural-interface.ts +1217 -73
- package/mcp-server/src/services/profiles.ts +428 -0
- package/mcp-server/src/services/secret-gate.ts +52 -0
- package/mcp-server/src/services/server-instructions.ts +122 -0
- package/mcp-server/src/services/sqlite.ts +1456 -823
- package/mcp-server/src/services/typesafe-bench.ts +247 -0
- package/mcp-server/src/services/typesafe-config.ts +790 -0
- package/mcp-server/src/services/typesafe.ts +390 -0
- package/mcp-server/src/services/vector-cache.ts +255 -0
- package/mcp-server/src/services/whiteboard-geometry.ts +711 -0
- package/mcp-server/src/tools/agents.ts +539 -0
- package/mcp-server/src/tools/bluesky.ts +1186 -0
- package/mcp-server/src/tools/browser-advanced.ts +15 -6
- package/mcp-server/src/tools/browser-batch.ts +178 -0
- package/mcp-server/src/tools/browser-cheatsheet.ts +155 -0
- package/mcp-server/src/tools/browser-interact.ts +362 -255
- package/mcp-server/src/tools/browser-navigate.ts +164 -77
- package/mcp-server/src/tools/browser-observe.ts +2222 -1330
- package/mcp-server/src/tools/browser.ts +181 -107
- package/mcp-server/src/tools/card.ts +7 -5
- package/mcp-server/src/tools/choice.ts +103 -0
- package/mcp-server/src/tools/computer.ts +190 -0
- package/mcp-server/src/tools/discord.ts +10 -23
- package/mcp-server/src/tools/fb-groups.ts +387 -0
- package/mcp-server/src/tools/forget.ts +1 -1
- package/mcp-server/src/tools/git.ts +3 -1
- package/mcp-server/src/tools/gsc/experience.ts +440 -0
- package/mcp-server/src/tools/gsc/helpers.ts +288 -0
- package/mcp-server/src/tools/gsc/indexing.ts +495 -0
- package/mcp-server/src/tools/gsc/inspect.ts +391 -0
- package/mcp-server/src/tools/gsc/navigate.ts +236 -0
- package/mcp-server/src/tools/gsc/performance.ts +352 -0
- package/mcp-server/src/tools/gsc/settings.ts +564 -0
- package/mcp-server/src/tools/gsc.ts +95 -0
- package/mcp-server/src/tools/image.ts +3 -1
- package/mcp-server/src/tools/leonardo-browser-tools.ts +47 -37
- package/mcp-server/src/tools/leonardo.ts +7 -5
- package/mcp-server/src/tools/loop.ts +118 -25
- package/mcp-server/src/tools/memories.ts +65 -5
- package/mcp-server/src/tools/morelogin.ts +81 -0
- package/mcp-server/src/tools/profile.ts +122 -0
- package/mcp-server/src/tools/recall.ts +105 -261
- package/mcp-server/src/tools/reflect.ts +171 -145
- package/mcp-server/src/tools/remember.ts +223 -115
- package/mcp-server/src/tools/style-guide-tools.ts +250 -0
- package/mcp-server/src/tools/style-guide.ts +16 -0
- package/mcp-server/src/tools/sync.ts +66 -9
- package/mcp-server/src/tools/tictactoe.ts +3 -1
- package/mcp-server/src/tools/utils.ts +22 -0
- package/mcp-server/src/tools/whiteboard-format.ts +394 -0
- package/mcp-server/src/tools/whiteboard-tools.ts +191 -183
- package/mcp-server/src/tools/whiteboard.ts +7 -5
- package/mcp-server/src/tools/youtube/analytics.ts +106 -0
- package/mcp-server/src/tools/youtube/dedup.ts +57 -0
- package/mcp-server/src/tools/youtube/download.ts +41 -0
- package/mcp-server/src/tools/youtube/helpers.ts +183 -0
- package/mcp-server/src/tools/youtube/media.ts +109 -0
- package/mcp-server/src/tools/youtube/metadata.ts +112 -0
- package/mcp-server/src/tools/youtube/navigate.ts +81 -0
- package/mcp-server/src/tools/youtube/playlist.ts +134 -0
- package/mcp-server/src/tools/youtube/schedule.ts +158 -0
- package/mcp-server/src/tools/youtube/source.ts +101 -0
- package/mcp-server/src/tools/youtube/upload.ts +296 -0
- package/mcp-server/src/tools/youtube.ts +47 -0
- package/mcp-server/src/tui.ts +8 -9
- package/neural-interface/games/TicTacToe/1.svg +1 -0
- package/neural-interface/games/TicTacToe/2.svg +1 -0
- package/neural-interface/games/TicTacToe/3.svg +1 -0
- package/neural-interface/games/TicTacToe/4.svg +1 -0
- package/neural-interface/games/TicTacToe/5.svg +1 -0
- package/neural-interface/games/TicTacToe/6.svg +1 -0
- package/neural-interface/games/TicTacToe/7.svg +1 -0
- package/neural-interface/games/TicTacToe/8.svg +1 -0
- package/neural-interface/games/TicTacToe/9.svg +1 -0
- package/neural-interface/games/TicTacToe/Board.svg +1 -0
- package/neural-interface/games/TicTacToe/Circle.svg +1 -0
- package/neural-interface/games/TicTacToe/Cross.svg +1 -0
- package/neural-interface/i18n/en.json +6983 -0
- package/neural-interface/i18n/pt-BR.json +6987 -0
- package/neural-interface/lib/assistant-api.js +1005 -0
- package/neural-interface/lib/assistant-attachments.js +121 -0
- package/neural-interface/lib/assistant-brains/claude.js +207 -0
- package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +43 -0
- package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +56 -0
- package/neural-interface/lib/assistant-brains/codex.js +213 -0
- package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +47 -0
- package/neural-interface/lib/assistant-brains/opencode-route-gate.js +62 -0
- package/neural-interface/lib/assistant-brains/opencode.js +320 -0
- package/neural-interface/lib/assistant-budget.js +318 -0
- package/neural-interface/lib/assistant-catalog.js +731 -0
- package/neural-interface/lib/assistant-clarify.js +664 -0
- package/neural-interface/lib/assistant-config.js +232 -0
- package/neural-interface/lib/assistant-dispatch.js +2744 -0
- package/neural-interface/lib/assistant-envelope.js +579 -0
- package/neural-interface/lib/assistant-evidence.js +236 -0
- package/neural-interface/lib/assistant-hidden-models.js +272 -0
- package/neural-interface/lib/assistant-jev.js +117 -0
- package/neural-interface/lib/assistant-judgments.js +111 -0
- package/neural-interface/lib/assistant-media.js +335 -0
- package/neural-interface/lib/assistant-memory.js +398 -0
- package/neural-interface/lib/assistant-persona.js +444 -0
- package/neural-interface/lib/assistant-plan-permissions.js +448 -0
- package/neural-interface/lib/assistant-playbooks/design.md +87 -0
- package/neural-interface/lib/assistant-playbooks.js +72 -0
- package/neural-interface/lib/assistant-pricing.js +159 -0
- package/neural-interface/lib/assistant-route-gate-codex.js +163 -0
- package/neural-interface/lib/assistant-route-gate.js +212 -0
- package/neural-interface/lib/assistant-router.js +1324 -0
- package/neural-interface/lib/assistant-runtime.js +3337 -0
- package/neural-interface/lib/assistant-task-prompt.js +472 -0
- package/neural-interface/lib/assistant-usage.js +1688 -0
- package/neural-interface/lib/augmented-path.js +50 -0
- package/neural-interface/lib/automation-storage-migration.js +54 -0
- package/neural-interface/lib/backup-service.js +851 -0
- package/neural-interface/lib/backup-zip-reader.js +262 -0
- package/neural-interface/lib/browser-assist-api.js +41 -0
- package/neural-interface/lib/browser-console.js +176 -0
- package/neural-interface/lib/browser-execution.js +241 -0
- package/neural-interface/lib/browser-extraction.js +191 -0
- package/neural-interface/lib/browser-screenshot.js +399 -0
- package/neural-interface/lib/browser-semantic-context.js +639 -0
- package/neural-interface/lib/browser-snapshots.js +192 -0
- package/neural-interface/lib/browser-tab-recovery.js +119 -0
- package/neural-interface/lib/browser-targets.js +70 -0
- package/neural-interface/lib/browser-tool-policy.js +926 -0
- package/neural-interface/lib/claude-accounts.js +230 -0
- package/neural-interface/lib/claude-agent-bridge.js +3296 -0
- package/neural-interface/lib/claude-bypass-policy.js +99 -0
- package/neural-interface/lib/claude-history.js +429 -0
- package/neural-interface/lib/claude-model-catalog.js +483 -0
- package/neural-interface/lib/claude-panel-requests.js +165 -0
- package/neural-interface/lib/claude-panel-session.js +272 -0
- package/neural-interface/lib/claude-permission-rules.js +153 -0
- package/neural-interface/lib/claude-session-cleanup.js +71 -0
- package/neural-interface/lib/claude-session-list.js +82 -0
- package/neural-interface/lib/claude-session-ops.js +140 -0
- package/neural-interface/lib/claude-settings-view.js +51 -0
- package/neural-interface/lib/claude-skin-connect.js +79 -0
- package/neural-interface/lib/claude-stall-policy.js +7 -0
- package/neural-interface/lib/claude-temporary.js +435 -0
- package/neural-interface/lib/cli-update-planner.js +312 -0
- package/neural-interface/lib/codex-capabilities.d.ts +41 -0
- package/neural-interface/lib/codex-capabilities.js +148 -0
- package/neural-interface/lib/codex-collaboration-mode.js +17 -0
- package/neural-interface/lib/codex-compat-rpc.js +241 -0
- package/neural-interface/lib/codex-mcp-approvals.js +56 -0
- package/neural-interface/lib/codex-mcp-apps.js +47 -0
- package/neural-interface/lib/codex-model-context.js +202 -0
- package/neural-interface/lib/codex-plan-store.js +71 -0
- package/neural-interface/lib/codex-request-journal.js +242 -0
- package/neural-interface/lib/codex-runtime-path.js +213 -0
- package/neural-interface/lib/codex-session-catalog.js +231 -0
- package/neural-interface/lib/codex-session-settings.js +46 -0
- package/neural-interface/lib/codex-sidepanel-permissions.js +110 -0
- package/neural-interface/lib/codex-thread-history.js +165 -0
- package/neural-interface/lib/codex-user-verification.js +34 -0
- package/neural-interface/lib/codex-writer-lifecycle.js +152 -0
- package/neural-interface/lib/confined-fs.js +256 -0
- package/neural-interface/lib/db.js +922 -0
- package/neural-interface/lib/design-md.js +9 -0
- package/neural-interface/lib/desktop/api.js +92 -0
- package/neural-interface/lib/desktop/audit.js +62 -0
- package/neural-interface/lib/desktop/build.js +423 -0
- package/neural-interface/lib/desktop/config.js +606 -0
- package/neural-interface/lib/desktop/coords.js +76 -0
- package/neural-interface/lib/desktop/fake-helper.js +1192 -0
- package/neural-interface/lib/desktop/frames.js +77 -0
- package/neural-interface/lib/desktop/grants.js +84 -0
- package/neural-interface/lib/desktop/guards.js +170 -0
- package/neural-interface/lib/desktop/helper/SynabunDesktop.swift +3056 -0
- package/neural-interface/lib/desktop/lease.js +51 -0
- package/neural-interface/lib/desktop/manager.js +545 -0
- package/neural-interface/lib/desktop/press-contexts.js +103 -0
- package/neural-interface/lib/desktop/protocol.js +495 -0
- package/neural-interface/lib/desktop/service.js +1202 -0
- package/neural-interface/lib/effort-levels.js +148 -0
- package/neural-interface/lib/event-loop-monitor.js +67 -0
- package/neural-interface/lib/exec-loop-command.js +209 -0
- package/neural-interface/lib/folder-picker.ps1 +77 -0
- package/neural-interface/lib/http-guards.js +287 -0
- package/neural-interface/lib/jsonl-lines.js +94 -0
- package/neural-interface/lib/loop-logger.js +127 -0
- package/neural-interface/lib/mcp-client-config.js +133 -0
- package/neural-interface/lib/mcp-installer.js +487 -0
- package/neural-interface/lib/mcp-profile-refresh-coordinator.js +78 -0
- package/neural-interface/lib/mcp-profile-servers.js +120 -0
- package/neural-interface/lib/memo-cache.js +84 -0
- package/neural-interface/lib/memory-api.js +758 -0
- package/neural-interface/lib/memory-map-api.js +1150 -0
- package/neural-interface/lib/memory-map-layout.js +872 -0
- package/neural-interface/lib/memory-map-worker.js +165 -0
- package/neural-interface/lib/memory-session-capture.js +81 -0
- package/neural-interface/lib/memory-timers.js +62 -0
- package/neural-interface/lib/morelogin.js +301 -0
- package/neural-interface/lib/native-binary-runtime.js +650 -0
- package/neural-interface/lib/native-loop-goal.js +33 -0
- package/neural-interface/lib/native-loop-providers.js +1016 -0
- package/neural-interface/lib/native-loop-runtime.js +747 -0
- package/neural-interface/lib/opencode-hidden-models.js +113 -0
- package/neural-interface/lib/opencode-history.js +253 -0
- package/neural-interface/lib/opencode-runtime-config.js +58 -0
- package/neural-interface/lib/opencode-v2-client.js +486 -0
- package/neural-interface/lib/opencode-v2-ws-requests.js +955 -0
- package/neural-interface/lib/path-confine.js +213 -0
- package/neural-interface/lib/plugin-installer.js +264 -0
- package/neural-interface/lib/project-storage.js +766 -0
- package/neural-interface/lib/pty-host/core.js +603 -0
- package/neural-interface/lib/pty-host/flow.js +43 -0
- package/neural-interface/lib/pty-host/host.js +47 -0
- package/neural-interface/lib/pty-host/manager.js +360 -0
- package/neural-interface/lib/pty-host/protocol.js +81 -0
- package/neural-interface/lib/pty-host/snapshot.js +133 -0
- package/neural-interface/lib/remote-policy.js +582 -0
- package/neural-interface/lib/rulesets/api.js +175 -0
- package/neural-interface/lib/rulesets/fs-safe.js +245 -0
- package/neural-interface/lib/rulesets/installer.js +1453 -0
- package/neural-interface/lib/rulesets/json-edit.js +186 -0
- package/neural-interface/lib/rulesets/lock.js +170 -0
- package/neural-interface/lib/rulesets/managed-text.js +314 -0
- package/neural-interface/lib/rulesets/normalise.js +24 -0
- package/neural-interface/lib/rulesets/render.js +84 -0
- package/neural-interface/lib/schedule-launch-intent.js +40 -0
- package/neural-interface/lib/session-cache-builder.js +524 -0
- package/neural-interface/lib/session-chunker.js +344 -0
- package/neural-interface/lib/session-indexer.js +327 -0
- package/neural-interface/lib/session-title-generator.js +227 -0
- package/neural-interface/lib/skill-prompt-bridge.js +11 -0
- package/neural-interface/lib/style-guide/api.js +375 -0
- package/neural-interface/lib/style-guide/color.js +352 -0
- package/neural-interface/lib/style-guide/export-css.js +99 -0
- package/neural-interface/lib/style-guide/export-dtcg.js +209 -0
- package/neural-interface/lib/style-guide/export-tailwind.js +102 -0
- package/neural-interface/lib/style-guide/fonts.json +227 -0
- package/neural-interface/lib/style-guide/import.js +1268 -0
- package/neural-interface/lib/style-guide/pointers.js +103 -0
- package/neural-interface/lib/style-guide/presets.js +232 -0
- package/neural-interface/lib/style-guide/render-design-md.js +319 -0
- package/neural-interface/lib/style-guide/schema.js +848 -0
- package/neural-interface/lib/style-guide/security.js +43 -0
- package/neural-interface/lib/style-guide/store.js +491 -0
- package/neural-interface/lib/style-guide/summary.js +106 -0
- package/neural-interface/lib/style-guide/tokens.js +170 -0
- package/neural-interface/lib/synabun-update-plan.js +305 -0
- package/neural-interface/lib/synabun-version.js +179 -0
- package/neural-interface/lib/system-restore.js +99 -0
- package/neural-interface/lib/terminal-env.js +86 -0
- package/neural-interface/lib/ui-blob-store.js +106 -0
- package/neural-interface/lib/vector-cache.js +2 -0
- package/neural-interface/lib/virtual-socket.js +60 -0
- package/neural-interface/lib/whatsapp/api.js +146 -0
- package/neural-interface/lib/whatsapp/auth-store.js +389 -0
- package/neural-interface/lib/whatsapp/baileys-adapter.js +457 -0
- package/neural-interface/lib/whatsapp/brain.js +128 -0
- package/neural-interface/lib/whatsapp/bridge.js +1499 -0
- package/neural-interface/lib/whatsapp/cards.js +519 -0
- package/neural-interface/lib/whatsapp/commands.js +92 -0
- package/neural-interface/lib/whatsapp/config.js +291 -0
- package/neural-interface/lib/whatsapp/connector/entry.mjs +25 -0
- package/neural-interface/lib/whatsapp/connector/manifest.json +6 -0
- package/neural-interface/lib/whatsapp/connector/npm-shrinkwrap.json +717 -0
- package/neural-interface/lib/whatsapp/connector/package.json +8 -0
- package/neural-interface/lib/whatsapp/fake-baileys.js +444 -0
- package/neural-interface/lib/whatsapp/format.js +552 -0
- package/neural-interface/lib/whatsapp/host-core.js +1225 -0
- package/neural-interface/lib/whatsapp/host.js +119 -0
- package/neural-interface/lib/whatsapp/identity.js +288 -0
- package/neural-interface/lib/whatsapp/inbound.js +149 -0
- package/neural-interface/lib/whatsapp/installer.js +562 -0
- package/neural-interface/lib/whatsapp/manager.js +638 -0
- package/neural-interface/lib/whatsapp/paths.js +81 -0
- package/neural-interface/lib/whatsapp/protocol.js +327 -0
- package/neural-interface/lib/whatsapp/qr-svg.js +57 -0
- package/neural-interface/lib/whatsapp/redact.js +130 -0
- package/neural-interface/lib/whatsapp/service.js +1305 -0
- package/neural-interface/lib/whatsapp/vendor/qrcodegen.js +848 -0
- package/neural-interface/package-lock.json +4682 -0
- package/neural-interface/package.json +58 -27
- package/neural-interface/public/apple-touch-icon.png +0 -0
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude.svg +1 -0
- package/neural-interface/public/favicon-16x16.png +0 -0
- package/neural-interface/public/favicon-32x32.png +0 -0
- package/neural-interface/public/icon-192.png +0 -0
- package/neural-interface/public/icon-512.png +0 -0
- package/neural-interface/public/index.html +32 -182
- package/neural-interface/public/index2d.html +2 -2
- package/neural-interface/public/logo-synabun.png +0 -0
- package/neural-interface/public/logoHD.png +0 -0
- package/neural-interface/public/mcp.svg +4 -0
- package/neural-interface/public/offline.html +335 -299
- package/neural-interface/public/onboarding.html +692 -155
- package/neural-interface/public/opencode-logo-dark-square.svg +1 -0
- package/neural-interface/public/shared/agent-runtime-options.js +596 -0
- package/neural-interface/public/shared/api.js +339 -28
- package/neural-interface/public/shared/assistant/asst-accounts.js +318 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +296 -0
- package/neural-interface/public/shared/assistant/asst-api.js +270 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +744 -0
- package/neural-interface/public/shared/assistant/asst-budget.js +333 -0
- package/neural-interface/public/shared/assistant/asst-composer.js +656 -0
- package/neural-interface/public/shared/assistant/asst-computer.js +803 -0
- package/neural-interface/public/shared/assistant/asst-control.js +1018 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +262 -0
- package/neural-interface/public/shared/assistant/asst-mascot.js +311 -0
- package/neural-interface/public/shared/assistant/asst-menu.js +375 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +1845 -0
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +589 -0
- package/neural-interface/public/shared/assistant/asst-render.js +3296 -0
- package/neural-interface/public/shared/assistant/asst-route.js +1653 -0
- package/neural-interface/public/shared/assistant/asst-roving.js +75 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +117 -0
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +488 -0
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +675 -0
- package/neural-interface/public/shared/assistant/asst-slash.js +258 -0
- package/neural-interface/public/shared/assistant/asst-state.js +727 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +1684 -0
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +1017 -0
- package/neural-interface/public/shared/assistant/asst-usage.js +444 -0
- package/neural-interface/public/shared/assistant/asst-ws.js +175 -0
- package/neural-interface/public/shared/automation-context.js +75 -0
- package/neural-interface/public/shared/blob-store.js +99 -0
- package/neural-interface/public/shared/cdx/cdx-capabilities.js +118 -0
- package/neural-interface/public/shared/cdx/cdx-cards.js +37 -0
- package/neural-interface/public/shared/cdx/cdx-commands.js +171 -0
- package/neural-interface/public/shared/cdx/cdx-context-menu.js +402 -0
- package/neural-interface/public/shared/cdx/cdx-context-model.js +220 -0
- package/neural-interface/public/shared/cdx/cdx-features.js +408 -0
- package/neural-interface/public/shared/cdx/cdx-icons.js +89 -0
- package/neural-interface/public/shared/cdx/cdx-mcp-app-host.js +414 -0
- package/neural-interface/public/shared/cdx/cdx-mcp-app-proxy.html +6 -0
- package/neural-interface/public/shared/cdx/cdx-mcp-app-proxy.js +56 -0
- package/neural-interface/public/shared/cdx/cdx-mcp-app.js +178 -0
- package/neural-interface/public/shared/cdx/cdx-model-catalog.js +120 -0
- package/neural-interface/public/shared/cdx/cdx-output-model.js +261 -0
- package/neural-interface/public/shared/cdx/cdx-output.js +382 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +1372 -0
- package/neural-interface/public/shared/cdx/cdx-plan.js +266 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +682 -0
- package/neural-interface/public/shared/cdx/cdx-render.js +3381 -0
- package/neural-interface/public/shared/cdx/cdx-requests.js +1136 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +3251 -0
- package/neural-interface/public/shared/cdx/cdx-tabs.js +7992 -0
- package/neural-interface/public/shared/cdx/cdx-ws.js +363 -0
- package/neural-interface/public/shared/cli-status.js +169 -0
- package/neural-interface/public/shared/constants.js +22 -5
- package/neural-interface/public/shared/cp/cp-agents.js +186 -0
- package/neural-interface/public/shared/cp/cp-bash.js +252 -0
- package/neural-interface/public/shared/cp/cp-compose.js +159 -0
- package/neural-interface/public/shared/cp/cp-context-menu.js +309 -0
- package/neural-interface/public/shared/cp/cp-context-model.js +157 -0
- package/neural-interface/public/shared/cp/cp-context-window.js +80 -0
- package/neural-interface/public/shared/cp/cp-ctx.js +11 -0
- package/neural-interface/public/shared/cp/cp-diff.js +255 -0
- package/neural-interface/public/shared/cp/cp-event-rows.js +637 -0
- package/neural-interface/public/shared/cp/cp-events.js +846 -0
- package/neural-interface/public/shared/cp/cp-markdown.js +104 -0
- package/neural-interface/public/shared/cp/cp-permission-model.js +905 -0
- package/neural-interface/public/shared/cp/cp-permissions.js +626 -0
- package/neural-interface/public/shared/cp/cp-rehydrate.js +113 -0
- package/neural-interface/public/shared/cp/cp-restore.js +224 -0
- package/neural-interface/public/shared/cp/cp-session-model.js +338 -0
- package/neural-interface/public/shared/cp/cp-session-settings.js +254 -0
- package/neural-interface/public/shared/cp/cp-sessions.js +227 -0
- package/neural-interface/public/shared/cp/cp-statusline.js +153 -0
- package/neural-interface/public/shared/cp/cp-styles.js +349 -0
- package/neural-interface/public/shared/cp/cp-tasks-model.js +282 -0
- package/neural-interface/public/shared/cp/cp-tasks.js +107 -0
- package/neural-interface/public/shared/cp/cp-temporary-view.js +56 -0
- package/neural-interface/public/shared/cp/cp-temporary.js +231 -0
- package/neural-interface/public/shared/cp/cp-tool-cards.js +326 -0
- package/neural-interface/public/shared/cp/cp-tool-results.js +618 -0
- package/neural-interface/public/shared/cp/cp-usage-model.js +124 -0
- package/neural-interface/public/shared/graph-activity.js +68 -0
- package/neural-interface/public/shared/html-shell.js +107 -12
- package/neural-interface/public/shared/i18n.js +132 -34
- package/neural-interface/public/shared/node-positions-store.js +44 -0
- package/neural-interface/public/shared/ocp-hidden-models.js +116 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +355 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-autoaccept-button.js +35 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-automation-ownership.js +72 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +280 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +67 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +179 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-changes.js +164 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +725 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +75 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +1204 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-confirm-logic.js +117 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-confirm.js +59 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-gauge.js +252 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +478 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +179 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +233 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-icons.js +30 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +1065 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-mention-hints.js +182 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +655 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +2636 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +601 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-profile-inheritance.js +78 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +649 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +35 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +357 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render-logic.js +189 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1869 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-select.js +42 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +117 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +1714 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +468 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-session-widgets.js +63 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +366 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-settings-link.js +19 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +343 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +746 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +347 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +338 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +3543 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-subagents-logic.js +482 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-tools-logic.js +351 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-tools.js +168 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-variantpicker.js +315 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +545 -0
- package/neural-interface/public/shared/provider-icons.js +30 -0
- package/neural-interface/public/shared/session-title.js +100 -0
- package/neural-interface/public/shared/settings/settings-ia.js +389 -0
- package/neural-interface/public/shared/settings/settings-kit.js +234 -0
- package/neural-interface/public/shared/settings/settings-search.js +463 -0
- package/neural-interface/public/shared/settings/settings-shell.js +570 -0
- package/neural-interface/public/shared/state.js +5 -0
- package/neural-interface/public/shared/storage.js +50 -7
- package/neural-interface/public/shared/styleguide/sg-agents.js +42 -0
- package/neural-interface/public/shared/styleguide/sg-brand.js +4 -0
- package/neural-interface/public/shared/styleguide/sg-color-client.js +388 -0
- package/neural-interface/public/shared/styleguide/sg-colors.js +148 -0
- package/neural-interface/public/shared/styleguide/sg-components.js +35 -0
- package/neural-interface/public/shared/styleguide/sg-history.js +41 -0
- package/neural-interface/public/shared/styleguide/sg-icons-logo.js +38 -0
- package/neural-interface/public/shared/styleguide/sg-imagery.js +4 -0
- package/neural-interface/public/shared/styleguide/sg-import-export.js +47 -0
- package/neural-interface/public/shared/styleguide/sg-kit.js +281 -0
- package/neural-interface/public/shared/styleguide/sg-labels.js +318 -0
- package/neural-interface/public/shared/styleguide/sg-layout.js +4 -0
- package/neural-interface/public/shared/styleguide/sg-motion.js +5 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +229 -0
- package/neural-interface/public/shared/styleguide/sg-preview.js +138 -0
- package/neural-interface/public/shared/styleguide/sg-rules.js +9 -0
- package/neural-interface/public/shared/styleguide/sg-shape.js +24 -0
- package/neural-interface/public/shared/styleguide/sg-state.js +132 -0
- package/neural-interface/public/shared/styleguide/sg-typography.js +124 -0
- package/neural-interface/public/shared/styles.css +7883 -1155
- package/neural-interface/public/shared/synabun-mascot.js +1625 -0
- package/neural-interface/public/shared/synabun-ticker.js +376 -0
- package/neural-interface/public/shared/term-renderer.js +343 -0
- package/neural-interface/public/shared/ui-assistant-panel.js +10 -0
- package/neural-interface/public/shared/ui-assistant.js +77 -0
- package/neural-interface/public/shared/ui-automation-studio.js +5105 -3573
- package/neural-interface/public/shared/ui-claude-panel.js +11685 -6333
- package/neural-interface/public/shared/ui-codex-panel.js +12 -0
- package/neural-interface/public/shared/ui-cost-widget.js +55 -34
- package/neural-interface/public/shared/ui-detail.js +77 -14
- package/neural-interface/public/shared/ui-explorer.js +360 -181
- package/neural-interface/public/shared/ui-file-explorer.js +485 -107
- package/neural-interface/public/shared/ui-help.js +2 -2
- package/neural-interface/public/shared/ui-image-gallery.js +45 -21
- package/neural-interface/public/shared/ui-judgments.js +1346 -0
- package/neural-interface/public/shared/ui-keybinds.js +3 -2
- package/neural-interface/public/shared/ui-link.js +105 -51
- package/neural-interface/public/shared/ui-loading.js +4 -4
- package/neural-interface/public/shared/ui-memory-maintenance.js +111 -0
- package/neural-interface/public/shared/ui-menubar.js +89 -4
- package/neural-interface/public/shared/ui-native-loop-router-state.js +94 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +493 -0
- package/neural-interface/public/shared/ui-native-window-id.js +7 -0
- package/neural-interface/public/shared/ui-navbar.js +357 -222
- package/neural-interface/public/shared/ui-notifications.js +58 -12
- package/neural-interface/public/shared/ui-opencode-panel-v2.js +17 -0
- package/neural-interface/public/shared/ui-panels.js +15 -7
- package/neural-interface/public/shared/ui-resume.js +823 -653
- package/neural-interface/public/shared/ui-schedule-queue.js +172 -0
- package/neural-interface/public/shared/ui-schedules-studio.js +1882 -0
- package/neural-interface/public/shared/ui-scroll-follow.js +194 -0
- package/neural-interface/public/shared/ui-sessions.js +789 -34
- package/neural-interface/public/shared/ui-settings.js +5793 -2190
- package/neural-interface/public/shared/ui-sidepanel-layout.js +109 -0
- package/neural-interface/public/shared/ui-sidepanel-runtime.js +19 -0
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +40 -0
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +221 -0
- package/neural-interface/public/shared/ui-sidepanel-state.js +131 -0
- package/neural-interface/public/shared/ui-sidepanel-tray.js +547 -0
- package/neural-interface/public/shared/ui-sidepanel-windows.js +387 -0
- package/neural-interface/public/shared/ui-skills.js +18 -32
- package/neural-interface/public/shared/ui-styleguide.js +35 -0
- package/neural-interface/public/shared/ui-sync.js +87 -0
- package/neural-interface/public/shared/ui-terminal.js +7877 -6385
- package/neural-interface/public/shared/ui-tool-updates.js +292 -0
- package/neural-interface/public/shared/ui-tooltip.js +156 -15
- package/neural-interface/public/shared/ui-tutorial-draw.js +119 -683
- package/neural-interface/public/shared/ui-tutorial-layout.js +135 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +59 -2209
- package/neural-interface/public/shared/ui-tutorial.css +102 -0
- package/neural-interface/public/shared/ui-tutorial.js +557 -1076
- package/neural-interface/public/shared/ui-update.js +420 -68
- package/neural-interface/public/shared/ui-whatsapp.js +941 -0
- package/neural-interface/public/shared/ui-whiteboard.js +1076 -233
- package/neural-interface/public/shared/ui-workspaces.js +2 -1
- package/neural-interface/public/shared/utils.js +58 -0
- package/neural-interface/public/shared/vterm-buffer.js +15 -5
- package/neural-interface/public/shared/whatsapp/wa-phone.js +65 -0
- package/neural-interface/public/shared/whatsapp/wa-view.js +411 -0
- package/neural-interface/public/sidepanel-session.html +18 -0
- package/neural-interface/public/sw.js +8 -2
- package/neural-interface/public/synabun.png +0 -0
- package/neural-interface/public/variant/2d/graph.js +1707 -1635
- package/neural-interface/public/variant/2d/lasso.js +2 -1
- package/neural-interface/public/variant/2d/layout.js +595 -594
- package/neural-interface/public/variant/2d/main.js +556 -521
- package/neural-interface/public/variant/2d/settings-gfx.js +18 -24
- package/neural-interface/public/variant/3d/main.js +516 -500
- package/neural-interface/public/variant/3d/map-controls.js +144 -0
- package/neural-interface/public/variant/3d/map-data.js +251 -0
- package/neural-interface/public/variant/3d/map-labels.js +241 -0
- package/neural-interface/public/variant/3d/map-pick.js +90 -0
- package/neural-interface/public/variant/3d/map-renderer.js +521 -0
- package/neural-interface/public/variant/3d/map-shaders.js +158 -0
- package/neural-interface/public/variant/3d/map-view.js +618 -0
- package/neural-interface/public/vendor/three/manifest.json +3 -0
- package/neural-interface/public/vendor/three/three-0.169.0.min.js +3844 -0
- package/neural-interface/public/vendor/xterm/manifest.json +3 -0
- package/neural-interface/public/vendor/xterm/xterm-6.0.0.css +285 -0
- package/neural-interface/public/vendor/xterm/xterm-6.0.0.min.js +192 -0
- package/neural-interface/scripts/build-vendor.mjs +64 -0
- package/neural-interface/scripts/codex-protocol-fixture.mjs +83 -0
- package/neural-interface/scripts/rebuild-pty.js +37 -23
- package/neural-interface/scripts/vendor-three-entry.js +24 -0
- package/neural-interface/scripts/vendor-xterm-entry.js +8 -0
- package/neural-interface/server.js +33948 -15000
- package/neural-interface/skins/default/skin.css +90 -0
- package/neural-interface/skins/default/skin.json +8 -0
- package/neural-interface/templates/rulesets/README.md +9 -0
- package/neural-interface/templates/rulesets/claude.md +3 -0
- package/neural-interface/templates/rulesets/codex.md +2 -0
- package/neural-interface/templates/rulesets/coexistence.md +6 -0
- package/neural-interface/templates/rulesets/core.md +26 -0
- package/neural-interface/templates/rulesets/cursor.md +2 -0
- package/neural-interface/templates/rulesets/gemini.md +2 -0
- package/neural-interface/templates/rulesets/legacy-hashes.json +227 -0
- package/neural-interface/templates/rulesets/manifest.json +12 -0
- package/neural-interface/templates/rulesets/opencode.md +2 -0
- package/package.json +119 -53
- package/postinstall.js +125 -26
- package/preuninstall.js +187 -0
- package/scripts/audit-claude-hook-usage.mjs +12 -0
- package/scripts/audit-memory-duplicates.mjs +44 -0
- package/scripts/measure-claude-hook-overhead.mjs +77 -0
- package/scripts/recover-user-config.mjs +254 -0
- package/scripts/restore-data-home-from-backup.mjs +239 -0
- package/setup.js +804 -260
- package/skills/leonardo/SKILL.md +19 -10
- package/skills/leonardo/modules/image-prompter.md +18 -12
- package/skills/leonardo/modules/video-prompter.md +19 -13
- package/skills/synabun/SKILL.md +209 -185
- package/skills/synabun/icon.png +0 -0
- package/skills/synabun/modules/audit.md +380 -374
- package/skills/synabun/modules/changelog.md +15 -6
- package/skills/synabun/modules/idea.md +148 -144
- package/skills/synabun/modules/memorize.md +13 -4
- package/skills/synabun/modules/schedule.md +198 -0
- package/skills/synabun/synabunicon.png +0 -0
- package/updater.cmd +31 -0
- package/updater.mjs +382 -0
- package/updater.sh +29 -0
- package/hooks/claude-code/test-user-learning.mjs +0 -531
- package/neural-interface/public/claude-chat.css +0 -878
- package/neural-interface/public/claude-chat.html +0 -92
- package/neural-interface/public/claude-chat.js +0 -1168
- package/neural-interface/public/index-legacy.html +0 -14677
- package/neural-interface/public/index2d-legacy.html +0 -8193
- package/neural-interface/public/shared/html-term-renderer.js +0 -1022
- package/neural-interface/public/variant/3d/background.js +0 -884
- package/neural-interface/public/variant/3d/camera.js +0 -661
- package/neural-interface/public/variant/3d/gfx.js +0 -116
- package/neural-interface/public/variant/3d/graph.js +0 -2399
- package/neural-interface/public/variant/3d/settings-gfx.js +0 -228
- package/neural-interface/templates/CLAUDE-template.md +0 -211
|
@@ -0,0 +1,1869 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// OpenCode V2 — Renderer
|
|
3
|
+
// Subscribes to state changes and reconciles the messages container. Handles
|
|
4
|
+
// every SDK part type (text, reasoning, tool, file, subtask, agent, retry,
|
|
5
|
+
// compaction, patch; step and snapshot markers are hidden), per-message
|
|
6
|
+
// errors, the retry banner, permission and question cards.
|
|
7
|
+
// What to show is decided in ocp-v2-render-logic.js (no DOM, unit-tested);
|
|
8
|
+
// this file builds the nodes. Markdown goes through `marked` and then the
|
|
9
|
+
// shared allowlist sanitizer before it reaches innerHTML.
|
|
10
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
import { getDefaultStore } from './ocp-v2-state.js';
|
|
13
|
+
import { api, supports, isDescendantSession, isKnownDescendantSession, onEvent } from './ocp-v2-ws.js';
|
|
14
|
+
import {
|
|
15
|
+
permissionCardView, ALWAYS_ALLOW_SCOPE, replyToPermission, FOREIGN_REQUEST_MESSAGE,
|
|
16
|
+
transcriptQuestionCard, answerQuestion, skipQuestion,
|
|
17
|
+
} from './ocp-v2-approvals.js';
|
|
18
|
+
import { captureBinding } from './ocp-v2-binding.js';
|
|
19
|
+
import { requestsVisibleIn } from './ocp-v2-rehydrate.js';
|
|
20
|
+
import { replyFailed, replyError } from './ocp-v2-caps.js';
|
|
21
|
+
import { handlePostPlanAction } from './ocp-v2-plan.js';
|
|
22
|
+
import { TOOL_ICONS } from './ocp-v2-icons.js';
|
|
23
|
+
import { renderToolCard } from './ocp-v2-tools.js';
|
|
24
|
+
import { messageMetaText } from './ocp-v2-tools-logic.js';
|
|
25
|
+
import { createStickyScrollController } from '../ui-scroll-follow.js';
|
|
26
|
+
import { sanitizeHtmlString } from '../assistant/asst-sanitize.js';
|
|
27
|
+
import { openProviderSettings } from './ocp-v2-settings-link.js';
|
|
28
|
+
import {
|
|
29
|
+
visibleParts, partSignature, messageErrorView, retryBannerView, retryPartText,
|
|
30
|
+
compactionLabel, inlinePartText, reasoningPreview, createCardDrafts,
|
|
31
|
+
} from './ocp-v2-render-logic.js';
|
|
32
|
+
import {
|
|
33
|
+
visibleMessageOrder, revertBannerView, messageText, messageActionsFor, deleteMessageConfirmText,
|
|
34
|
+
} from './ocp-v2-sessions-logic.js';
|
|
35
|
+
import {
|
|
36
|
+
revertToMessage, restoreReverted, retryFromMessage, forkSession, deleteMessage,
|
|
37
|
+
} from './ocp-v2-session-actions.js';
|
|
38
|
+
|
|
39
|
+
const _expandedReasoningIds = new Set();
|
|
40
|
+
const _activeRenderers = new Set();
|
|
41
|
+
// The callbacks each panel passed to mountRenderer, for part renderers that
|
|
42
|
+
// only get the store.
|
|
43
|
+
const _rendererOpts = new WeakMap();
|
|
44
|
+
|
|
45
|
+
// ── Markdown via marked (matches the V1 ocp panel) ──────────────────────────
|
|
46
|
+
let _marked = null;
|
|
47
|
+
let _markedLoading = false;
|
|
48
|
+
|
|
49
|
+
function ensureMarked() {
|
|
50
|
+
if (_marked || _markedLoading || typeof window === 'undefined') return;
|
|
51
|
+
_markedLoading = true;
|
|
52
|
+
import('https://cdn.jsdelivr.net/npm/marked@14/lib/marked.esm.js')
|
|
53
|
+
.then((mod) => {
|
|
54
|
+
_marked = mod?.marked || mod?.default || null;
|
|
55
|
+
if (_marked?.setOptions) _marked.setOptions({ breaks: true, gfm: true });
|
|
56
|
+
// Re-render every active panel so messages mounted before marked loaded
|
|
57
|
+
// get formatted.
|
|
58
|
+
for (const r of _activeRenderers) {
|
|
59
|
+
try { r.render(); } catch {}
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
.catch(() => {})
|
|
63
|
+
.finally(() => { _markedLoading = false; });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
ensureMarked();
|
|
67
|
+
|
|
68
|
+
// Each mountRenderer call creates an independent renderer instance bound to
|
|
69
|
+
// its own container + store. Multiple panels can render simultaneously.
|
|
70
|
+
// opts (all optional; a sub-agent panel passes none and gets Copy only):
|
|
71
|
+
// onOpenSession(session) show a session this panel just created (fork)
|
|
72
|
+
// onComposeText(text, { files, resources, resourceList }) put a prompt back into the compose box (undo)
|
|
73
|
+
// onSendText(text, { files }) send a prompt with its own file parts (retry); resolves true when sent
|
|
74
|
+
// onOpenChild(sessionId) show the sub-agent session of a task card
|
|
75
|
+
// confirm({ key, text, confirmLabel }) ask in the panel before something is
|
|
76
|
+
// destroyed; resolves true only after the second
|
|
77
|
+
// click (ocp-v2-confirm-logic.js). Without it a
|
|
78
|
+
// message is not deleted.
|
|
79
|
+
export function mountRenderer(containerEl, store = getDefaultStore(), opts = {}) {
|
|
80
|
+
_rendererOpts.set(store, opts);
|
|
81
|
+
let _container = containerEl;
|
|
82
|
+
let _renderScheduled = false;
|
|
83
|
+
const scrollController = createStickyScrollController(_container);
|
|
84
|
+
|
|
85
|
+
const doRender = () => {
|
|
86
|
+
render(_container, store, opts);
|
|
87
|
+
scrollController?.scrollToBottom();
|
|
88
|
+
};
|
|
89
|
+
const scheduleRender = () => {
|
|
90
|
+
if (_renderScheduled) return;
|
|
91
|
+
_renderScheduled = true;
|
|
92
|
+
requestAnimationFrame(() => {
|
|
93
|
+
_renderScheduled = false;
|
|
94
|
+
if (_container) doRender();
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const unsubscribe = store.subscribe((event, state) => {
|
|
99
|
+
// The server listed this session's open permission requests: a reason
|
|
100
|
+
// typed for one that is no longer among them has no card to come back to.
|
|
101
|
+
if (event?.type === 'permission:set' && event.listed) settlePermissionReasons(state);
|
|
102
|
+
scheduleRender();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const instance = {
|
|
106
|
+
render: doRender,
|
|
107
|
+
unmount: () => {
|
|
108
|
+
try { unsubscribe(); } catch {}
|
|
109
|
+
scrollController?.destroy();
|
|
110
|
+
_activeRenderers.delete(instance);
|
|
111
|
+
_container = null;
|
|
112
|
+
},
|
|
113
|
+
scrollToBottom: (options) => scrollController?.scrollToBottom(options),
|
|
114
|
+
isFollowing: () => scrollController?.isFollowing() ?? false,
|
|
115
|
+
};
|
|
116
|
+
_activeRenderers.add(instance);
|
|
117
|
+
doRender();
|
|
118
|
+
return instance;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Back-compat shim — primary panel callers may still expect an unmount API.
|
|
122
|
+
export function unmountRenderer() {
|
|
123
|
+
for (const r of _activeRenderers) { try { r.unmount(); } catch {} }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const _partSig = partSignature;
|
|
127
|
+
|
|
128
|
+
function _msgSig(msg) {
|
|
129
|
+
let sig = `m:${msg.role || 'assistant'}`;
|
|
130
|
+
for (const part of visibleParts(msg)) sig += '|' + _partSig(part);
|
|
131
|
+
return sig;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Reconcile container children against `desired` (ordered list). Reuses DOM
|
|
135
|
+
// nodes whose ocpv2Sig still matches — replaces, inserts, or moves the rest.
|
|
136
|
+
// Replaces the previous "wipe innerHTML and rebuild" hot path that thrashed
|
|
137
|
+
// the transcript on every store delta.
|
|
138
|
+
function _reconcile(container, desired) {
|
|
139
|
+
const existing = new Map();
|
|
140
|
+
for (const child of [...container.children]) {
|
|
141
|
+
const k = child.dataset?.ocpv2Key;
|
|
142
|
+
if (k) existing.set(k, child);
|
|
143
|
+
else child.remove();
|
|
144
|
+
}
|
|
145
|
+
const wanted = new Set(desired.map(d => d.key));
|
|
146
|
+
for (const [k, el] of existing) {
|
|
147
|
+
if (!wanted.has(k)) { el.remove(); existing.delete(k); }
|
|
148
|
+
}
|
|
149
|
+
let prev = null;
|
|
150
|
+
for (const { key, sig, build } of desired) {
|
|
151
|
+
let node = existing.get(key);
|
|
152
|
+
if (!node || node.dataset.ocpv2Sig !== sig) {
|
|
153
|
+
const fresh = build();
|
|
154
|
+
if (!fresh) continue;
|
|
155
|
+
fresh.dataset.ocpv2Key = key;
|
|
156
|
+
fresh.dataset.ocpv2Sig = sig;
|
|
157
|
+
if (node) {
|
|
158
|
+
container.replaceChild(fresh, node);
|
|
159
|
+
} else if (prev) {
|
|
160
|
+
if (prev.nextSibling) container.insertBefore(fresh, prev.nextSibling);
|
|
161
|
+
else container.appendChild(fresh);
|
|
162
|
+
} else if (container.firstChild) {
|
|
163
|
+
container.insertBefore(fresh, container.firstChild);
|
|
164
|
+
} else {
|
|
165
|
+
container.appendChild(fresh);
|
|
166
|
+
}
|
|
167
|
+
node = fresh;
|
|
168
|
+
} else {
|
|
169
|
+
const expected = prev ? prev.nextSibling : container.firstChild;
|
|
170
|
+
if (node !== expected) {
|
|
171
|
+
if (prev) {
|
|
172
|
+
if (prev.nextSibling) container.insertBefore(node, prev.nextSibling);
|
|
173
|
+
else container.appendChild(node);
|
|
174
|
+
} else if (container.firstChild) {
|
|
175
|
+
container.insertBefore(node, container.firstChild);
|
|
176
|
+
} else {
|
|
177
|
+
container.appendChild(node);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
prev = node;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function _buildEmpty() {
|
|
186
|
+
const empty = document.createElement('div');
|
|
187
|
+
empty.className = 'ocpv2-empty ocpv2-empty-brand';
|
|
188
|
+
empty.innerHTML =
|
|
189
|
+
'<svg class="ocpv2-empty-logo" viewBox="0 0 240 300" fill="currentColor" aria-hidden="true">'
|
|
190
|
+
+ '<path fill-rule="evenodd" d="M0 0h240v300H0V0zm30 30v240h180V30H30z"/>'
|
|
191
|
+
+ '<rect x="30" y="150" width="180" height="120" opacity=".45"/>'
|
|
192
|
+
+ '</svg>'
|
|
193
|
+
+ '<div class="ocpv2-empty-name">OpenCode</div>';
|
|
194
|
+
return empty;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function render(_container, store, opts = {}) {
|
|
198
|
+
if (!_container) return;
|
|
199
|
+
const s = store.getState();
|
|
200
|
+
const actionContext = {
|
|
201
|
+
supports,
|
|
202
|
+
canOpenSessions: typeof opts.onOpenSession === 'function',
|
|
203
|
+
canCompose: typeof opts.onComposeText === 'function' && typeof opts.onSendText === 'function',
|
|
204
|
+
};
|
|
205
|
+
const revertBanner = revertBannerView(s.sessionInfo, s.messageOrder);
|
|
206
|
+
|
|
207
|
+
// A card is drawn only for a request of the session on screen or of one of
|
|
208
|
+
// its sub-agents: whatever else is in the queue is not this panel's to answer.
|
|
209
|
+
const permissions = requestsVisibleIn(
|
|
210
|
+
s.pendingPermissions?.length ? s.pendingPermissions : (s.pendingPermission ? [s.pendingPermission] : []),
|
|
211
|
+
s.sessionId, isKnownDescendantSession,
|
|
212
|
+
);
|
|
213
|
+
const questions = requestsVisibleIn(s.pendingQuestions, s.sessionId, isKnownDescendantSession);
|
|
214
|
+
const retry = retryBannerView(s.sessionStatus);
|
|
215
|
+
const hasInteractiveCards = permissions.length > 0 || questions.length > 0 || !!s.showPostPlanActions;
|
|
216
|
+
if (s.messageOrder.length === 0 && !hasInteractiveCards && !s.errors.length && !retry && !revertBanner) {
|
|
217
|
+
_reconcile(_container, [{ key: '__empty__', sig: 'empty', build: _buildEmpty }]);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const desired = [];
|
|
222
|
+
|
|
223
|
+
let prevAssistantId = null;
|
|
224
|
+
// What was undone (session.revert) stays out of sight until it is restored.
|
|
225
|
+
for (const messageId of visibleMessageOrder(s.messageOrder, s.sessionInfo?.revert)) {
|
|
226
|
+
const msg = s.messages.get(messageId);
|
|
227
|
+
if (!msg) continue;
|
|
228
|
+
const parts = visibleParts(msg);
|
|
229
|
+
if ((msg.role || 'assistant') === 'user') {
|
|
230
|
+
// A user message made only of text OpenCode wrote itself (a shell run,
|
|
231
|
+
// a compaction marker's context) has nothing of the user's to show.
|
|
232
|
+
if (!parts.length) continue;
|
|
233
|
+
prevAssistantId = null;
|
|
234
|
+
// The marker OpenCode leaves where it compacted is a divider, not
|
|
235
|
+
// something the user said.
|
|
236
|
+
if (parts.every((part) => part.type === 'compaction')) {
|
|
237
|
+
desired.push({
|
|
238
|
+
key: `u:${msg.id}`,
|
|
239
|
+
sig: `compaction|${_partSig(parts[0])}`,
|
|
240
|
+
build: () => renderStepMarker(compactionLabel(parts[0])),
|
|
241
|
+
});
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const userActions = messageActionsFor(msg, s, actionContext);
|
|
245
|
+
desired.push({
|
|
246
|
+
key: `u:${msg.id}`,
|
|
247
|
+
sig: `${_msgSig(msg)}|${userActions.join(',')}`,
|
|
248
|
+
build: () => renderMessage(msg, store, userActions, opts),
|
|
249
|
+
});
|
|
250
|
+
} else {
|
|
251
|
+
for (const part of parts) {
|
|
252
|
+
const isContinuation = prevAssistantId === msg.id;
|
|
253
|
+
const partKey = part.id || `${part.type}:${part.index ?? ''}`;
|
|
254
|
+
desired.push({
|
|
255
|
+
key: `a:${msg.id}:${partKey}`,
|
|
256
|
+
sig: `${isContinuation ? 'c' : 'h'}|${msg.info?.summary ? 's' : ''}|${_partSig(part)}`,
|
|
257
|
+
build: () => renderAssistantPartBubble(msg, part, isContinuation, store),
|
|
258
|
+
});
|
|
259
|
+
prevAssistantId = msg.id;
|
|
260
|
+
}
|
|
261
|
+
// Under a finished assistant message: what OpenCode says it ran on and
|
|
262
|
+
// cost, then the actions.
|
|
263
|
+
const done = !!msg.info?.time?.completed;
|
|
264
|
+
const assistantActions = parts.length && done ? messageActionsFor(msg, s, actionContext) : [];
|
|
265
|
+
const meta = parts.length && done ? messageMetaText(msg.info) : '';
|
|
266
|
+
if (assistantActions.length || meta) {
|
|
267
|
+
desired.push({
|
|
268
|
+
key: `a:${msg.id}:actions`,
|
|
269
|
+
sig: `act:${assistantActions.join(',')}|${meta}`,
|
|
270
|
+
build: () => renderAssistantActions(msg, store, assistantActions, opts, meta),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
const errorView = messageErrorView(msg.info);
|
|
274
|
+
if (errorView) {
|
|
275
|
+
desired.push({
|
|
276
|
+
key: `a:${msg.id}:error`,
|
|
277
|
+
sig: `e:${errorView.kind}:${errorView.text}`,
|
|
278
|
+
build: () => renderMessageError(msg, errorView, store),
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (revertBanner) {
|
|
285
|
+
desired.push({
|
|
286
|
+
key: 'revert',
|
|
287
|
+
sig: `revert:${revertBanner.text}:${revertBanner.hasFileChanges ? 'f' : ''}:${s.running ? 'r' : ''}`,
|
|
288
|
+
build: () => renderRevertBanner(revertBanner, store),
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// What /help shows: a card of the panel's own, under the transcript.
|
|
293
|
+
if (s.helpCard) {
|
|
294
|
+
desired.push({
|
|
295
|
+
key: 'help',
|
|
296
|
+
sig: `help:${JSON.stringify(s.helpCard)}`,
|
|
297
|
+
build: () => renderHelpCard(s.helpCard, store),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Errors sit at the end of the transcript, where the reader is, and each
|
|
302
|
+
// one can be dismissed.
|
|
303
|
+
for (const err of s.errors) {
|
|
304
|
+
desired.push({
|
|
305
|
+
key: `err:${err.id || err.at}`,
|
|
306
|
+
sig: `err:${String(err?.message || err || '')}`,
|
|
307
|
+
build: () => renderErrorBanner(err, store),
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (retry) {
|
|
312
|
+
desired.push({
|
|
313
|
+
key: 'retry',
|
|
314
|
+
sig: `retry:${retry.title}:${retry.message}:${retry.nextAt}:${retry.actionLink}`,
|
|
315
|
+
build: () => renderRetryBanner(retry),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Every pending request gets its own card, oldest first: a second request
|
|
320
|
+
// (a parallel sub-agent, a second tool) no longer replaces the first.
|
|
321
|
+
for (const p of permissions) {
|
|
322
|
+
desired.push({
|
|
323
|
+
key: `perm:${p.id || p.requestId || 'pending'}`,
|
|
324
|
+
sig: `perm:${p.id || ''}:${p.sessionID || ''}:${p.state || ''}:${p._auto ? 'auto' : ''}:${supports('feature:permission-reply-message') ? 'r' : ''}${supports('permission:saved:list') ? 's' : ''}`,
|
|
325
|
+
build: () => renderPermissionCard(p, store),
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// PLAN COMPLETE card pushes BEFORE pending questions so that when the agent
|
|
330
|
+
// emits both a plan body and a follow-up AskUserQuestion they stack with
|
|
331
|
+
// PLAN COMPLETE on top of the question card.
|
|
332
|
+
if (s.showPostPlanActions) {
|
|
333
|
+
const planLen = (s.editedPlanContent || s.planContent || '').length;
|
|
334
|
+
desired.push({
|
|
335
|
+
key: 'postplan',
|
|
336
|
+
sig: `pp:${s.postPlanHeader || ''}:${planLen}:${s.planMaterializing ? '1' : '0'}:${s.planFilePath || ''}`,
|
|
337
|
+
build: () => renderPostPlanCard(s, store),
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
for (const q of questions) {
|
|
342
|
+
desired.push({
|
|
343
|
+
key: `q:${q.id || q.requestId || ''}`,
|
|
344
|
+
sig: `q:${q.id || ''}:${(q.questions?.length || 0)}:${q.answered ? '1' : '0'}`,
|
|
345
|
+
build: () => renderQuestionCard(q, store),
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
_reconcile(_container, desired);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// The /help card (helpCardView): the plan card's frame around what the
|
|
353
|
+
// composer can do. Built from data with textContent; nothing of it is a
|
|
354
|
+
// message, and Close takes it away.
|
|
355
|
+
function renderHelpCard(view, store) {
|
|
356
|
+
const wrap = document.createElement('div');
|
|
357
|
+
wrap.className = 'ocpv2-post-plan-card ocpv2-help-card';
|
|
358
|
+
const head = document.createElement('div');
|
|
359
|
+
head.className = 'ocpv2-post-plan-header';
|
|
360
|
+
head.textContent = view.title || 'Help';
|
|
361
|
+
wrap.appendChild(head);
|
|
362
|
+
if (view.note) {
|
|
363
|
+
const note = document.createElement('div');
|
|
364
|
+
note.className = 'ocpv2-post-plan-note';
|
|
365
|
+
note.textContent = view.note;
|
|
366
|
+
wrap.appendChild(note);
|
|
367
|
+
}
|
|
368
|
+
for (const section of Array.isArray(view.sections) ? view.sections : []) {
|
|
369
|
+
const title = document.createElement('div');
|
|
370
|
+
title.className = 'ocpv2-session-menu-note';
|
|
371
|
+
title.textContent = section.title || '';
|
|
372
|
+
wrap.appendChild(title);
|
|
373
|
+
for (const row of section.rows || []) {
|
|
374
|
+
const line = document.createElement('div');
|
|
375
|
+
line.className = 'ocpv2-help-row';
|
|
376
|
+
const name = document.createElement('code');
|
|
377
|
+
name.className = 'ocpv2-help-name';
|
|
378
|
+
name.textContent = row.name || '';
|
|
379
|
+
const text = document.createElement('span');
|
|
380
|
+
text.className = 'ocpv2-help-text';
|
|
381
|
+
text.textContent = row.text || '';
|
|
382
|
+
line.append(name, text);
|
|
383
|
+
if (row.source) {
|
|
384
|
+
const source = document.createElement('span');
|
|
385
|
+
source.className = 'ocpv2-session-item-meta';
|
|
386
|
+
source.textContent = row.source;
|
|
387
|
+
line.appendChild(source);
|
|
388
|
+
}
|
|
389
|
+
wrap.appendChild(line);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const actions = document.createElement('div');
|
|
393
|
+
actions.className = 'ocpv2-post-plan-actions';
|
|
394
|
+
const close = document.createElement('button');
|
|
395
|
+
close.type = 'button';
|
|
396
|
+
close.className = 'ocpv2-post-plan-btn';
|
|
397
|
+
close.textContent = 'Close';
|
|
398
|
+
close.addEventListener('click', () => store.setHelpCard?.(null));
|
|
399
|
+
actions.appendChild(close);
|
|
400
|
+
wrap.appendChild(actions);
|
|
401
|
+
return wrap;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function renderPostPlanCard(s, store) {
|
|
405
|
+
const wrap = document.createElement('div');
|
|
406
|
+
wrap.className = 'ocpv2-post-plan-card';
|
|
407
|
+
|
|
408
|
+
const head = document.createElement('div');
|
|
409
|
+
head.className = 'ocpv2-post-plan-header';
|
|
410
|
+
head.textContent = s.postPlanHeader || 'PLAN COMPLETE';
|
|
411
|
+
wrap.appendChild(head);
|
|
412
|
+
|
|
413
|
+
const note = document.createElement('div');
|
|
414
|
+
note.className = 'ocpv2-post-plan-note';
|
|
415
|
+
note.textContent = 'Plan ready. Continue into implementation, keep planning, compact, or reopen the plan in the editor.';
|
|
416
|
+
wrap.appendChild(note);
|
|
417
|
+
|
|
418
|
+
const planText = String(s.editedPlanContent || s.planContent || '').trim();
|
|
419
|
+
if (planText) {
|
|
420
|
+
const content = document.createElement('div');
|
|
421
|
+
content.className = 'ocpv2-post-plan-content';
|
|
422
|
+
content.innerHTML = renderMarkdown(planText);
|
|
423
|
+
wrap.appendChild(content);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (s.planFilePath) {
|
|
427
|
+
const saved = document.createElement('div');
|
|
428
|
+
saved.className = 'ocpv2-post-plan-saved';
|
|
429
|
+
const rel = String(s.planFilePath).replace(/^.*\/(data\/plans\/[^/]+\/[^/]+)$/, '$1');
|
|
430
|
+
saved.textContent = `Saved to ${rel || s.planFilePath}`;
|
|
431
|
+
wrap.appendChild(saved);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const actions = document.createElement('div');
|
|
435
|
+
actions.className = 'ocpv2-post-plan-actions';
|
|
436
|
+
wrap.appendChild(actions);
|
|
437
|
+
|
|
438
|
+
const addButton = (label, action, cls = '', disabled = false) => {
|
|
439
|
+
const btn = document.createElement('button');
|
|
440
|
+
btn.type = 'button';
|
|
441
|
+
btn.className = `ocpv2-post-plan-btn ${cls}`.trim();
|
|
442
|
+
btn.textContent = label;
|
|
443
|
+
btn.disabled = !!disabled;
|
|
444
|
+
btn.addEventListener('click', () => {
|
|
445
|
+
// The plan card is this binding's: so is what its action reports.
|
|
446
|
+
const at = captureBinding(store);
|
|
447
|
+
handlePostPlanAction(action, store).catch((err) => {
|
|
448
|
+
if (at.isCurrent()) store.pushError({ message: err?.message || 'Plan action failed' });
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
actions.appendChild(btn);
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
addButton('Continue with implementation', 'continue', 'primary');
|
|
455
|
+
addButton('Continue planning', 'continue-planning');
|
|
456
|
+
addButton(s.planMaterializing ? 'Preparing plan...' : 'Edit plan', 'edit', '', s.planMaterializing);
|
|
457
|
+
addButton('Compact context', 'compact');
|
|
458
|
+
|
|
459
|
+
return wrap;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// ── Per-message render ───────────────────────────────────────────────────────
|
|
463
|
+
|
|
464
|
+
function renderMessage(msg, store, actions = [], opts = {}) {
|
|
465
|
+
const wrap = document.createElement('div');
|
|
466
|
+
wrap.className = `ocpv2-msg ocpv2-msg-${msg.role || 'assistant'}`;
|
|
467
|
+
wrap.dataset.messageId = msg.id;
|
|
468
|
+
|
|
469
|
+
const role = document.createElement('div');
|
|
470
|
+
role.className = 'ocpv2-msg-role';
|
|
471
|
+
role.textContent = msg.role === 'user' ? 'You' : 'OpenCode';
|
|
472
|
+
wrap.appendChild(role);
|
|
473
|
+
|
|
474
|
+
for (const part of visibleParts(msg)) {
|
|
475
|
+
const el = renderPart(part, store);
|
|
476
|
+
if (el) wrap.appendChild(el);
|
|
477
|
+
}
|
|
478
|
+
const bar = renderMessageActions(msg, store, actions, opts);
|
|
479
|
+
if (bar) wrap.appendChild(bar);
|
|
480
|
+
return wrap;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ── Message actions (copy / undo / fork / delete / retry) ───────────────────
|
|
484
|
+
|
|
485
|
+
const ACTION_LABELS = {
|
|
486
|
+
copy: 'Copy',
|
|
487
|
+
undo: 'Undo to here',
|
|
488
|
+
fork: 'Fork from here',
|
|
489
|
+
delete: 'Delete',
|
|
490
|
+
retry: 'Retry',
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
function renderMessageActions(msg, store, actions, opts) {
|
|
494
|
+
if (!actions || !actions.length) return null;
|
|
495
|
+
const bar = document.createElement('div');
|
|
496
|
+
bar.className = 'ocpv2-msg-actions';
|
|
497
|
+
for (const action of actions) {
|
|
498
|
+
const btn = document.createElement('button');
|
|
499
|
+
btn.type = 'button';
|
|
500
|
+
btn.className = 'ocpv2-msg-action';
|
|
501
|
+
btn.dataset.action = action;
|
|
502
|
+
btn.textContent = ACTION_LABELS[action] || action;
|
|
503
|
+
btn.addEventListener('click', async (event) => {
|
|
504
|
+
event.stopPropagation();
|
|
505
|
+
btn.disabled = true;
|
|
506
|
+
// A failure is reported in the session the action was clicked in.
|
|
507
|
+
const at = captureBinding(store);
|
|
508
|
+
try { await runMessageAction(action, msg, store, opts, btn); }
|
|
509
|
+
catch (err) { if (at.isCurrent()) store.pushError({ message: err?.message || `${ACTION_LABELS[action] || action} failed` }); }
|
|
510
|
+
finally { btn.disabled = false; }
|
|
511
|
+
});
|
|
512
|
+
bar.appendChild(btn);
|
|
513
|
+
}
|
|
514
|
+
return bar;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// The actions of an assistant message hang under its last bubble.
|
|
518
|
+
function renderAssistantActions(msg, store, actions, opts, meta = '') {
|
|
519
|
+
const wrap = document.createElement('div');
|
|
520
|
+
wrap.className = 'ocpv2-msg ocpv2-msg-assistant ocpv2-msg-continuation ocpv2-msg-actions-row';
|
|
521
|
+
wrap.dataset.messageId = msg.id;
|
|
522
|
+
if (meta) {
|
|
523
|
+
const line = document.createElement('div');
|
|
524
|
+
line.className = 'ocpv2-msg-meta';
|
|
525
|
+
line.textContent = meta;
|
|
526
|
+
line.setAttribute('data-tooltip', 'Model, agent, tokens and cost as reported by OpenCode');
|
|
527
|
+
wrap.appendChild(line);
|
|
528
|
+
}
|
|
529
|
+
const bar = renderMessageActions(msg, store, actions, opts);
|
|
530
|
+
if (bar) wrap.appendChild(bar);
|
|
531
|
+
return wrap;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
async function copyText(text, btn, done = 'Copied') {
|
|
535
|
+
await navigator.clipboard.writeText(text);
|
|
536
|
+
if (!btn) return;
|
|
537
|
+
const label = btn.textContent;
|
|
538
|
+
btn.textContent = done;
|
|
539
|
+
setTimeout(() => { if (btn.isConnected) btn.textContent = label; }, 1200);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async function runMessageAction(action, msg, store, opts, btn) {
|
|
543
|
+
// `moved`: the panel went to another session while the action was out. Its
|
|
544
|
+
// result is dropped: no banner, no prompt in the composer, nothing sent.
|
|
545
|
+
const report = (result, fallback) => {
|
|
546
|
+
if (result && result.ok === false && !result.cancelled && !result.moved) store.pushError({ message: result.error || fallback });
|
|
547
|
+
return result;
|
|
548
|
+
};
|
|
549
|
+
switch (action) {
|
|
550
|
+
case 'copy':
|
|
551
|
+
await copyText(messageText(msg), btn);
|
|
552
|
+
return;
|
|
553
|
+
case 'undo': {
|
|
554
|
+
const result = report(await revertToMessage(store, api, msg.id), 'Undo failed');
|
|
555
|
+
if (result?.ok && (result.text || result.files?.length)) {
|
|
556
|
+
opts.onComposeText?.(result.text, { files: result.files, resources: result.resources, resourceList: result.resourceList });
|
|
557
|
+
}
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
case 'retry':
|
|
561
|
+
report(await retryFromMessage(store, api, msg.id, (text, extra) => opts.onSendText(text, extra)), 'Retry failed');
|
|
562
|
+
return;
|
|
563
|
+
case 'fork': {
|
|
564
|
+
// The click is a navigation (opts.onNavigate hands out its intent): the
|
|
565
|
+
// fork takes the panel only while it is still the user's latest choice.
|
|
566
|
+
const nav = opts.onNavigate?.();
|
|
567
|
+
const result = report(await forkSession(store, api, msg.id), 'Fork failed');
|
|
568
|
+
// A fork made while the user moved on is kept as a tab, not put on screen.
|
|
569
|
+
if (result?.ok) await opts.onOpenSession?.(result.session, { activate: !result.moved, nav });
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
case 'delete': {
|
|
573
|
+
// Two steps, both in the panel: this click only asks, naming the
|
|
574
|
+
// message. A panel that cannot ask deletes nothing.
|
|
575
|
+
const agreed = typeof opts.confirm === 'function' && (await opts.confirm({
|
|
576
|
+
key: `message-delete:${msg.id}`, text: deleteMessageConfirmText(msg), confirmLabel: 'Delete message',
|
|
577
|
+
})) === true;
|
|
578
|
+
if (!agreed) return;
|
|
579
|
+
report(await deleteMessage(store, api, msg.id), 'Delete failed');
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
default:
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// What session.revert undid, with the way back.
|
|
587
|
+
function renderRevertBanner(view, store) {
|
|
588
|
+
const el = document.createElement('div');
|
|
589
|
+
el.className = 'ocpv2-retry-banner ocpv2-revert-banner';
|
|
590
|
+
const text = document.createElement('span');
|
|
591
|
+
text.className = 'ocpv2-retry-message';
|
|
592
|
+
text.textContent = view.hasFileChanges ? `${view.text} File changes of those turns were rolled back too.` : view.text;
|
|
593
|
+
el.appendChild(text);
|
|
594
|
+
if (supports('session:unrevert')) {
|
|
595
|
+
const restore = inlineAction('Restore', async () => {
|
|
596
|
+
const result = await restoreReverted(store, api);
|
|
597
|
+
if (!result.ok && !result.moved) store.pushError({ message: result.error || 'Restore failed' });
|
|
598
|
+
}, store);
|
|
599
|
+
restore.disabled = !!store.getState().running;
|
|
600
|
+
el.appendChild(restore);
|
|
601
|
+
}
|
|
602
|
+
return el;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// One assistant bubble per part — each tool use / text / reasoning row reads
|
|
606
|
+
// as its own response. Continuation bubbles (subsequent parts of the same
|
|
607
|
+
// message) drop the avatar + role so the column reads as one calm thread.
|
|
608
|
+
function renderAssistantPartBubble(msg, part, isContinuation, store) {
|
|
609
|
+
if (part.type === 'step-start' || part.type === 'step-finish') return null;
|
|
610
|
+
const partEl = renderPart(part, store);
|
|
611
|
+
if (!partEl) return null;
|
|
612
|
+
|
|
613
|
+
const wrap = document.createElement('div');
|
|
614
|
+
const cls = ['ocpv2-msg', 'ocpv2-msg-assistant', `ocpv2-msg-part-${part.type || 'other'}`];
|
|
615
|
+
if (isContinuation) cls.push('ocpv2-msg-continuation');
|
|
616
|
+
wrap.className = cls.join(' ');
|
|
617
|
+
wrap.dataset.messageId = msg.id;
|
|
618
|
+
wrap.dataset.partId = part.id || '';
|
|
619
|
+
|
|
620
|
+
if (!isContinuation) {
|
|
621
|
+
const role = document.createElement('div');
|
|
622
|
+
role.className = 'ocpv2-msg-role';
|
|
623
|
+
// The assistant message that follows a compaction is its summary.
|
|
624
|
+
role.textContent = msg.info?.summary ? 'OpenCode · summary' : 'OpenCode';
|
|
625
|
+
wrap.appendChild(role);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
wrap.appendChild(partEl);
|
|
629
|
+
return wrap;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function renderPart(part, store) {
|
|
633
|
+
switch (part.type) {
|
|
634
|
+
case 'text': return renderTextPart(part);
|
|
635
|
+
case 'reasoning': return renderReasoningPart(part);
|
|
636
|
+
case 'tool': return renderToolPart(part, store);
|
|
637
|
+
case 'file': return renderFilePart(part);
|
|
638
|
+
case 'step-start': return renderStepMarker('Step start');
|
|
639
|
+
case 'step-finish': return renderStepMarker('Step finish');
|
|
640
|
+
case 'compaction': return renderStepMarker(compactionLabel(part));
|
|
641
|
+
case 'retry': return renderNotePart(retryPartText(part), 'ocpv2-part-note-warn');
|
|
642
|
+
case 'subtask':
|
|
643
|
+
case 'agent':
|
|
644
|
+
case 'patch': return renderNotePart(inlinePartText(part));
|
|
645
|
+
default: return renderUnknownPart(part);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// ── Part renderers ───────────────────────────────────────────────────────────
|
|
650
|
+
|
|
651
|
+
function renderTextPart(part) {
|
|
652
|
+
const el = document.createElement('div');
|
|
653
|
+
el.className = 'ocpv2-part ocpv2-part-text';
|
|
654
|
+
el.innerHTML = renderMarkdown(part.text || '');
|
|
655
|
+
decorateCodeBlocks(el);
|
|
656
|
+
return el;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// A Copy button on every fenced block.
|
|
660
|
+
function decorateCodeBlocks(root) {
|
|
661
|
+
for (const pre of root.querySelectorAll('pre')) {
|
|
662
|
+
const btn = document.createElement('button');
|
|
663
|
+
btn.type = 'button';
|
|
664
|
+
btn.className = 'ocpv2-code-copy';
|
|
665
|
+
btn.textContent = 'Copy';
|
|
666
|
+
btn.addEventListener('click', (event) => {
|
|
667
|
+
event.stopPropagation();
|
|
668
|
+
const code = pre.querySelector('code');
|
|
669
|
+
copyText((code || pre).textContent || '', btn).catch(() => {});
|
|
670
|
+
});
|
|
671
|
+
pre.classList.add('ocpv2-code-block');
|
|
672
|
+
pre.appendChild(btn);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function renderReasoningPart(part) {
|
|
677
|
+
const streaming = !part?.time?.end;
|
|
678
|
+
// While streaming → single thin row with an animated "Thinking…" label
|
|
679
|
+
// (no body, no container — matches CLI feel).
|
|
680
|
+
// After streaming → click-to-expand "Thought" details for review.
|
|
681
|
+
if (streaming) {
|
|
682
|
+
const row = document.createElement('div');
|
|
683
|
+
row.className = 'ocpv2-part ocpv2-thinking-row';
|
|
684
|
+
const dot = document.createElement('span');
|
|
685
|
+
dot.className = 'ocpv2-thinking-dot';
|
|
686
|
+
const label = document.createElement('span');
|
|
687
|
+
label.className = 'ocpv2-thinking-label';
|
|
688
|
+
label.textContent = 'Thinking';
|
|
689
|
+
const ellipsis = document.createElement('span');
|
|
690
|
+
ellipsis.className = 'ocpv2-thinking-ellipsis';
|
|
691
|
+
ellipsis.innerHTML = '<span>.</span><span>.</span><span>.</span>';
|
|
692
|
+
row.appendChild(dot);
|
|
693
|
+
row.appendChild(label);
|
|
694
|
+
row.appendChild(ellipsis);
|
|
695
|
+
// Reasoning streams in as deltas: show where the model's thinking is now.
|
|
696
|
+
const preview = reasoningPreview(part.text);
|
|
697
|
+
if (!preview) return row;
|
|
698
|
+
const live = document.createElement('div');
|
|
699
|
+
live.className = 'ocpv2-part ocpv2-thinking-live';
|
|
700
|
+
live.appendChild(row);
|
|
701
|
+
const text = document.createElement('div');
|
|
702
|
+
text.className = 'ocpv2-thinking-preview';
|
|
703
|
+
text.textContent = preview;
|
|
704
|
+
live.appendChild(text);
|
|
705
|
+
return live;
|
|
706
|
+
}
|
|
707
|
+
const wrap = document.createElement('details');
|
|
708
|
+
wrap.className = 'ocpv2-part ocpv2-part-reasoning';
|
|
709
|
+
const key = reasoningPartKey(part);
|
|
710
|
+
if (_expandedReasoningIds.has(key)) wrap.open = true;
|
|
711
|
+
const summary = document.createElement('summary');
|
|
712
|
+
summary.className = 'ocpv2-reasoning-summary';
|
|
713
|
+
summary.addEventListener('click', () => {
|
|
714
|
+
if (wrap.open) _expandedReasoningIds.delete(key);
|
|
715
|
+
else _expandedReasoningIds.add(key);
|
|
716
|
+
});
|
|
717
|
+
const label = document.createElement('span');
|
|
718
|
+
label.textContent = 'Thought';
|
|
719
|
+
summary.appendChild(label);
|
|
720
|
+
wrap.appendChild(summary);
|
|
721
|
+
wrap.addEventListener('toggle', () => {
|
|
722
|
+
if (wrap.open) _expandedReasoningIds.add(key);
|
|
723
|
+
else _expandedReasoningIds.delete(key);
|
|
724
|
+
});
|
|
725
|
+
const body = document.createElement('div');
|
|
726
|
+
body.className = 'ocpv2-reasoning-body';
|
|
727
|
+
body.innerHTML = renderMarkdown(part.text || '');
|
|
728
|
+
wrap.appendChild(body);
|
|
729
|
+
return wrap;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function reasoningPartKey(part) {
|
|
733
|
+
const msgId = part?.messageID || part?.messageId || 'message';
|
|
734
|
+
const partId = part?.id || part?.partID || part?.partId || '';
|
|
735
|
+
if (partId) return `${msgId}:${partId}`;
|
|
736
|
+
return `${msgId}:reasoning:${String(part?.text || '').slice(0, 120)}`;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
const QUESTION_TOOL_NAMES = new Set(['question', 'askuserquestion', 'ask_user_question', 'ask_user', 'user_question']);
|
|
740
|
+
|
|
741
|
+
function isQuestionToolName(name) {
|
|
742
|
+
const key = String(name || '').toLowerCase().replace(/[^a-z_]/g, '');
|
|
743
|
+
return QUESTION_TOOL_NAMES.has(key);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function extractQuestionsFromToolInput(input) {
|
|
747
|
+
if (!input || typeof input !== 'object') return [];
|
|
748
|
+
if (Array.isArray(input.questions) && input.questions.length) return input.questions;
|
|
749
|
+
if (input.question || Array.isArray(input.options)) return [input];
|
|
750
|
+
return [];
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// ── SynaBun MCP tool branding ────────────────────────────────────────────────
|
|
754
|
+
const SYNABUN_LOGO_HTML = '<img src="logoHD.png" alt="" style="width:14px;height:14px;object-fit:contain;">';
|
|
755
|
+
|
|
756
|
+
const SYNABUN_LABELS = {
|
|
757
|
+
recall: 'Recall', remember: 'Remember', reflect: 'Reflect',
|
|
758
|
+
forget: 'Forget', restore: 'Restore', memories: 'Memories', sync: 'Sync',
|
|
759
|
+
category: 'Category', loop: 'Loop', git: 'Git', tictactoe: 'TicTacToe',
|
|
760
|
+
image_staged: 'Images', profile: 'Profile',
|
|
761
|
+
browser_navigate: 'Browser · Navigate', browser_screenshot: 'Browser · Screenshot', browser_console: 'Browser · Console',
|
|
762
|
+
browser_snapshot: 'Browser · Snapshot', browser_content: 'Browser · Content',
|
|
763
|
+
browser_click: 'Browser · Click', browser_type: 'Browser · Type',
|
|
764
|
+
browser_fill: 'Browser · Fill', browser_hover: 'Browser · Hover',
|
|
765
|
+
browser_select: 'Browser · Select', browser_press: 'Browser · Press',
|
|
766
|
+
browser_scroll: 'Browser · Scroll', browser_wait: 'Browser · Wait',
|
|
767
|
+
browser_go_back: 'Browser · Back', browser_go_forward: 'Browser · Forward',
|
|
768
|
+
browser_reload: 'Browser · Reload', browser_evaluate: 'Browser · Evaluate',
|
|
769
|
+
browser_upload: 'Browser · Upload', browser_session: 'Browser · Session',
|
|
770
|
+
browser_cheatsheet: 'Browser · Cheatsheet',
|
|
771
|
+
browser_extract_tweets: 'Extract · Tweets', browser_extract_fb_posts: 'Extract · Facebook',
|
|
772
|
+
browser_fb_composer_state: 'Facebook · Composer', browser_extract_fb_groups: 'Extract · FB Groups',
|
|
773
|
+
browser_extract_ig_feed: 'Extract · Instagram', browser_extract_ig_post: 'Extract · IG Post',
|
|
774
|
+
browser_extract_ig_reels: 'Extract · IG Reels', browser_extract_ig_profile: 'Extract · IG Profile',
|
|
775
|
+
browser_extract_ig_search: 'Extract · IG Search',
|
|
776
|
+
browser_extract_tiktok_videos: 'Extract · TikTok', browser_extract_tiktok_search: 'Extract · TikTok Search',
|
|
777
|
+
browser_extract_tiktok_studio: 'Extract · TikTok Studio', browser_extract_tiktok_profile: 'Extract · TikTok Profile',
|
|
778
|
+
browser_extract_li_feed: 'Extract · LinkedIn', browser_extract_li_profile: 'Extract · LI Profile',
|
|
779
|
+
browser_extract_li_post: 'Extract · LI Post', browser_extract_li_notifications: 'Extract · LI Notifications',
|
|
780
|
+
browser_extract_li_messages: 'Extract · LI Messages', browser_extract_li_search_people: 'Extract · LI People',
|
|
781
|
+
browser_extract_li_network: 'Extract · LI Network', browser_extract_li_jobs: 'Extract · LI Jobs',
|
|
782
|
+
browser_extract_wa_chats: 'Extract · WhatsApp', browser_extract_wa_messages: 'Extract · WA Messages',
|
|
783
|
+
bluesky_session: 'BlueSky · Session', bluesky_timeline: 'BlueSky · Timeline',
|
|
784
|
+
bluesky_author_feed: 'BlueSky · Author Feed', bluesky_thread: 'BlueSky · Thread',
|
|
785
|
+
bluesky_profile: 'BlueSky · Profile', bluesky_search_posts: 'BlueSky · Search Posts',
|
|
786
|
+
bluesky_search_actors: 'BlueSky · Search Users', bluesky_notifications: 'BlueSky · Notifications',
|
|
787
|
+
bluesky_graph: 'BlueSky · Graph', bluesky_likes: 'BlueSky · Likes', bluesky_feed: 'BlueSky · Feed',
|
|
788
|
+
bluesky_post: 'BlueSky · Post', bluesky_action: 'BlueSky · Action',
|
|
789
|
+
bluesky_resolve: 'BlueSky · Resolve', bluesky_dm: 'BlueSky · DM',
|
|
790
|
+
whiteboard_read: 'Whiteboard · Read', whiteboard_add: 'Whiteboard · Add',
|
|
791
|
+
whiteboard_update: 'Whiteboard · Update', whiteboard_remove: 'Whiteboard · Remove',
|
|
792
|
+
whiteboard_screenshot: 'Whiteboard · Screenshot',
|
|
793
|
+
card_list: 'Cards · List', card_open: 'Cards · Open', card_close: 'Cards · Close',
|
|
794
|
+
card_update: 'Cards · Update', card_screenshot: 'Cards · Screenshot',
|
|
795
|
+
discord_guild: 'Discord · Guild', discord_channel: 'Discord · Channel',
|
|
796
|
+
discord_role: 'Discord · Role', discord_message: 'Discord · Message',
|
|
797
|
+
discord_member: 'Discord · Member', discord_onboarding: 'Discord · Onboarding',
|
|
798
|
+
discord_webhook: 'Discord · Webhook', discord_thread: 'Discord · Thread',
|
|
799
|
+
leonardo_browser_navigate: 'Leonardo · Navigate', leonardo_browser_generate: 'Leonardo · Generate',
|
|
800
|
+
leonardo_browser_library: 'Leonardo · Library', leonardo_browser_download: 'Leonardo · Download',
|
|
801
|
+
leonardo_browser_reference: 'Leonardo · Reference',
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
function isSynaBunTool(name) {
|
|
805
|
+
return typeof name === 'string' && (name.startsWith('mcp__SynaBun__') || name.startsWith('SynaBun_'));
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function synabunToolKey(name) {
|
|
809
|
+
return (name || '').replace(/^mcp__SynaBun__/, '').replace(/^SynaBun_/, '');
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function synabunDisplayName(name) {
|
|
813
|
+
const key = synabunToolKey(name);
|
|
814
|
+
return SYNABUN_LABELS[key] || (key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, ' '));
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
function _truncate(s, n = 90) {
|
|
818
|
+
const str = String(s || '').replace(/\s+/g, ' ').trim();
|
|
819
|
+
return str.length > n ? str.slice(0, n - 1) + '…' : str;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function synabunSummaryText(name, input) {
|
|
823
|
+
if (!input || typeof input !== 'object') return '';
|
|
824
|
+
const key = synabunToolKey(name);
|
|
825
|
+
const t = (s) => _truncate(s, 90);
|
|
826
|
+
switch (key) {
|
|
827
|
+
case 'recall': return t(input.query);
|
|
828
|
+
case 'remember': return t(input.content);
|
|
829
|
+
case 'reflect': return t(input.content || input.memory_id);
|
|
830
|
+
case 'forget':
|
|
831
|
+
case 'restore': return t(input.memory_id);
|
|
832
|
+
case 'memories': return t(input.action);
|
|
833
|
+
case 'category': return t(`${input.action || ''}${input.name ? ' · ' + input.name : ''}`);
|
|
834
|
+
case 'sync': return '';
|
|
835
|
+
case 'git': return t(input.action);
|
|
836
|
+
case 'loop': return t(`${input.action || ''}${input.prompt ? ' · ' + input.prompt : ''}`);
|
|
837
|
+
case 'browser_navigate': return t(input.url);
|
|
838
|
+
case 'browser_click':
|
|
839
|
+
case 'browser_hover': return t(input.selector || input.label);
|
|
840
|
+
case 'browser_type':
|
|
841
|
+
case 'browser_fill': return t(input.text || input.value || input.selector);
|
|
842
|
+
case 'browser_select': return t(input.value || input.selector);
|
|
843
|
+
case 'browser_press': return t(input.key);
|
|
844
|
+
case 'browser_scroll': return t(input.direction || input.selector);
|
|
845
|
+
case 'browser_evaluate': return t(input.script);
|
|
846
|
+
case 'browser_session': return t(input.action);
|
|
847
|
+
case 'image_staged': return t(input.action);
|
|
848
|
+
case 'discord_message': return t(input.content || input.action);
|
|
849
|
+
case 'discord_channel':
|
|
850
|
+
case 'discord_member':
|
|
851
|
+
case 'discord_role': return t(input.action || input.name);
|
|
852
|
+
case 'whiteboard_add':
|
|
853
|
+
case 'whiteboard_update': return t(input.content || input.title);
|
|
854
|
+
case 'card_open':
|
|
855
|
+
case 'card_update': return t(input.title || input.id);
|
|
856
|
+
case 'leonardo_browser_generate': return t(input.prompt);
|
|
857
|
+
default: return '';
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Persist expand/collapse state across re-renders (render() rebuilds DOM).
|
|
862
|
+
const _expandedToolIds = new Set();
|
|
863
|
+
const SYNABUN_CHEVRON_SVG = '<svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>';
|
|
864
|
+
|
|
865
|
+
function renderSynaBunToolPart(part, toolName, stateStatus) {
|
|
866
|
+
const callId = part.callID || part.id || part.toolCallId || part.tool_use_id || `sb-${toolName}`;
|
|
867
|
+
const expanded = _expandedToolIds.has(callId);
|
|
868
|
+
|
|
869
|
+
const wrap = document.createElement('div');
|
|
870
|
+
wrap.className = 'ocpv2-part ocpv2-part-tool ocpv2-tool-synabun' + (expanded ? ' ocpv2-tool-expanded' : '');
|
|
871
|
+
wrap.dataset.toolId = callId;
|
|
872
|
+
wrap.dataset.status = stateStatus;
|
|
873
|
+
|
|
874
|
+
const head = document.createElement('div');
|
|
875
|
+
head.className = 'ocpv2-tool-head ocpv2-sb-head';
|
|
876
|
+
|
|
877
|
+
const icon = document.createElement('span');
|
|
878
|
+
icon.className = 'ocpv2-sb-icon';
|
|
879
|
+
icon.innerHTML = SYNABUN_LOGO_HTML;
|
|
880
|
+
head.appendChild(icon);
|
|
881
|
+
|
|
882
|
+
const titles = document.createElement('span');
|
|
883
|
+
titles.className = 'ocpv2-sb-titles';
|
|
884
|
+
const name = document.createElement('span');
|
|
885
|
+
name.className = 'ocpv2-tool-name ocpv2-sb-name';
|
|
886
|
+
name.textContent = synabunDisplayName(toolName);
|
|
887
|
+
titles.appendChild(name);
|
|
888
|
+
|
|
889
|
+
const input = part.state?.input ?? part.input;
|
|
890
|
+
const summary = synabunSummaryText(toolName, input || {});
|
|
891
|
+
if (summary) {
|
|
892
|
+
const sub = document.createElement('span');
|
|
893
|
+
sub.className = 'ocpv2-sb-summary';
|
|
894
|
+
sub.textContent = summary;
|
|
895
|
+
titles.appendChild(sub);
|
|
896
|
+
}
|
|
897
|
+
head.appendChild(titles);
|
|
898
|
+
|
|
899
|
+
const status = document.createElement('span');
|
|
900
|
+
status.className = `ocpv2-tool-status ocpv2-tool-${stateStatus}`;
|
|
901
|
+
status.textContent = stateStatus;
|
|
902
|
+
head.appendChild(status);
|
|
903
|
+
|
|
904
|
+
const chevron = document.createElement('span');
|
|
905
|
+
chevron.className = 'ocpv2-sb-chevron';
|
|
906
|
+
chevron.innerHTML = SYNABUN_CHEVRON_SVG;
|
|
907
|
+
head.appendChild(chevron);
|
|
908
|
+
|
|
909
|
+
head.addEventListener('click', () => {
|
|
910
|
+
if (_expandedToolIds.has(callId)) {
|
|
911
|
+
_expandedToolIds.delete(callId);
|
|
912
|
+
wrap.classList.remove('ocpv2-tool-expanded');
|
|
913
|
+
} else {
|
|
914
|
+
_expandedToolIds.add(callId);
|
|
915
|
+
wrap.classList.add('ocpv2-tool-expanded');
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
wrap.appendChild(head);
|
|
919
|
+
|
|
920
|
+
const body = document.createElement('div');
|
|
921
|
+
body.className = 'ocpv2-sb-body';
|
|
922
|
+
|
|
923
|
+
if (input !== undefined) {
|
|
924
|
+
const inp = document.createElement('div');
|
|
925
|
+
inp.className = 'ocpv2-tool-input';
|
|
926
|
+
inp.textContent = formatJson(input);
|
|
927
|
+
body.appendChild(inp);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
const output = part.state?.output ?? part.output;
|
|
931
|
+
const error = part.state?.error ?? part.error;
|
|
932
|
+
if (error) {
|
|
933
|
+
const errEl = document.createElement('div');
|
|
934
|
+
errEl.className = 'ocpv2-tool-output ocpv2-tool-output-error';
|
|
935
|
+
errEl.textContent = typeof error === 'string' ? error : formatJson(error);
|
|
936
|
+
body.appendChild(errEl);
|
|
937
|
+
} else if (output !== undefined && output !== null) {
|
|
938
|
+
appendToolOutput(body, output);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
wrap.appendChild(body);
|
|
942
|
+
return wrap;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// Render an MCP tool result. Tools like browser_screenshot / card_screenshot /
|
|
946
|
+
// whiteboard_screenshot / leonardo_* return `{ content: [text, image] }`,
|
|
947
|
+
// which arrives here as an array (or a JSON string of one). Render image
|
|
948
|
+
// blocks as actual <img> elements; render text blocks as their text; fall
|
|
949
|
+
// back to formatJson for anything else.
|
|
950
|
+
function appendToolOutput(body, output) {
|
|
951
|
+
const blocks = parseMcpContentBlocks(output);
|
|
952
|
+
if (blocks) {
|
|
953
|
+
for (const b of blocks) {
|
|
954
|
+
if (b.type === 'image' && b.data) {
|
|
955
|
+
const wrap = document.createElement('div');
|
|
956
|
+
wrap.className = 'ocpv2-tool-output ocpv2-tool-output-image';
|
|
957
|
+
const img = document.createElement('img');
|
|
958
|
+
img.className = 'ocpv2-tool-screenshot';
|
|
959
|
+
img.src = `data:${b.mimeType || b.media_type || 'image/jpeg'};base64,${b.data}`;
|
|
960
|
+
img.alt = 'screenshot';
|
|
961
|
+
img.loading = 'lazy';
|
|
962
|
+
wrap.appendChild(img);
|
|
963
|
+
body.appendChild(wrap);
|
|
964
|
+
} else if ((b.type === 'text' || b.text) && b.text) {
|
|
965
|
+
const t = document.createElement('div');
|
|
966
|
+
t.className = 'ocpv2-tool-output';
|
|
967
|
+
t.textContent = String(b.text);
|
|
968
|
+
body.appendChild(t);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
const out = document.createElement('div');
|
|
974
|
+
out.className = 'ocpv2-tool-output';
|
|
975
|
+
out.textContent = typeof output === 'string' ? output : formatJson(output);
|
|
976
|
+
body.appendChild(out);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
function parseMcpContentBlocks(output) {
|
|
980
|
+
let arr = null;
|
|
981
|
+
if (Array.isArray(output)) arr = output;
|
|
982
|
+
else if (output && typeof output === 'object' && Array.isArray(output.content)) arr = output.content;
|
|
983
|
+
else if (typeof output === 'string') {
|
|
984
|
+
const trimmed = output.trim();
|
|
985
|
+
if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
|
|
986
|
+
try {
|
|
987
|
+
const parsed = JSON.parse(trimmed);
|
|
988
|
+
if (Array.isArray(parsed)) arr = parsed;
|
|
989
|
+
else if (parsed && Array.isArray(parsed.content)) arr = parsed.content;
|
|
990
|
+
} catch {}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
if (!arr || !arr.length) return null;
|
|
994
|
+
// Only treat as MCP content if at least one block has a recognisable shape.
|
|
995
|
+
const looksMcp = arr.some((b) => b && (b.type === 'image' || b.type === 'text' || typeof b.text === 'string'));
|
|
996
|
+
return looksMcp ? arr : null;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
function renderToolPart(part, store) {
|
|
1000
|
+
const toolName = part.tool || part.name || '';
|
|
1001
|
+
const stateStatus = part.state?.status || part.status || 'pending';
|
|
1002
|
+
|
|
1003
|
+
// OpenCode's `question` tool may arrive both as a tool part and as a
|
|
1004
|
+
// `question.asked` request. The reply must go through `/question/{id}/reply`;
|
|
1005
|
+
// sending a normal prompt only queues behind the running turn.
|
|
1006
|
+
if (isQuestionToolName(toolName)) {
|
|
1007
|
+
if (stateStatus === 'completed' || stateStatus === 'error') {
|
|
1008
|
+
const done = document.createElement('div');
|
|
1009
|
+
done.className = 'ocpv2-part ocpv2-part-question-stub';
|
|
1010
|
+
done.textContent = 'Question answered.';
|
|
1011
|
+
return done;
|
|
1012
|
+
}
|
|
1013
|
+
// Dedup: if an SSE-driven QuestionRequest exists whose tool.callID matches
|
|
1014
|
+
// this tool part, let the pendingQuestions render handle it instead.
|
|
1015
|
+
const partCallId = part.callID || part.id;
|
|
1016
|
+
const sseMatch = (store.getState().pendingQuestions || []).find(
|
|
1017
|
+
(q) => q?.tool?.callID && partCallId && String(q.tool.callID) === String(partCallId)
|
|
1018
|
+
);
|
|
1019
|
+
if (sseMatch) {
|
|
1020
|
+
const stub = document.createElement('div');
|
|
1021
|
+
stub.className = 'ocpv2-part ocpv2-part-question-stub';
|
|
1022
|
+
stub.textContent = 'Awaiting your answer…';
|
|
1023
|
+
return stub;
|
|
1024
|
+
}
|
|
1025
|
+
const input = part.state?.input ?? part.input ?? {};
|
|
1026
|
+
const questions = extractQuestionsFromToolInput(input);
|
|
1027
|
+
if (!questions.length) {
|
|
1028
|
+
const stub = document.createElement('div');
|
|
1029
|
+
stub.className = 'ocpv2-part ocpv2-part-question-stub';
|
|
1030
|
+
stub.textContent = 'Awaiting your answer…';
|
|
1031
|
+
return stub;
|
|
1032
|
+
}
|
|
1033
|
+
// The card belongs to the session the part is in. A part of any other
|
|
1034
|
+
// session (however it reached this store) gets no card to answer from.
|
|
1035
|
+
const synthetic = transcriptQuestionCard(store, part, questions);
|
|
1036
|
+
if (!synthetic) {
|
|
1037
|
+
const stub = document.createElement('div');
|
|
1038
|
+
stub.className = 'ocpv2-part ocpv2-part-question-stub';
|
|
1039
|
+
stub.textContent = 'Awaiting your answer…';
|
|
1040
|
+
return stub;
|
|
1041
|
+
}
|
|
1042
|
+
if (isQuestionSubmitted(synthetic.id)) {
|
|
1043
|
+
const done = document.createElement('div');
|
|
1044
|
+
done.className = 'ocpv2-part ocpv2-part-question-stub';
|
|
1045
|
+
done.textContent = 'Question answer submitted.';
|
|
1046
|
+
return done;
|
|
1047
|
+
}
|
|
1048
|
+
return renderQuestionCard(synthetic, store);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
if (isSynaBunTool(toolName)) {
|
|
1052
|
+
return renderSynaBunToolPart(part, toolName, stateStatus);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// Everything else: a collapsible card (ocp-v2-tools.js).
|
|
1056
|
+
return renderToolCard(part, {
|
|
1057
|
+
contentBlocks: (output) => {
|
|
1058
|
+
if (!parseMcpContentBlocks(output)) return null;
|
|
1059
|
+
const holder = document.createElement('div');
|
|
1060
|
+
appendToolOutput(holder, output);
|
|
1061
|
+
return holder;
|
|
1062
|
+
},
|
|
1063
|
+
onOpenChild: _rendererOpts.get(store)?.onOpenChild || null,
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
function renderFilePart(part) {
|
|
1068
|
+
const mime = part.mime || part.media_type || part.mediaType || part.contentType || '';
|
|
1069
|
+
const url = part.url || part.dataUrl || '';
|
|
1070
|
+
const filename = part.filename || part.name || 'image';
|
|
1071
|
+
const isImage = (typeof mime === 'string' && mime.startsWith('image/'))
|
|
1072
|
+
|| /^data:image\//.test(url);
|
|
1073
|
+
|
|
1074
|
+
if (isImage && url) {
|
|
1075
|
+
const wrap = document.createElement('div');
|
|
1076
|
+
wrap.className = 'ocpv2-part ocpv2-part-image-wrap';
|
|
1077
|
+
const img = document.createElement('img');
|
|
1078
|
+
img.className = 'ocpv2-part-image';
|
|
1079
|
+
img.src = url;
|
|
1080
|
+
img.alt = filename;
|
|
1081
|
+
img.loading = 'lazy';
|
|
1082
|
+
wrap.appendChild(img);
|
|
1083
|
+
return wrap;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
const el = document.createElement('div');
|
|
1087
|
+
el.className = 'ocpv2-part ocpv2-part-file';
|
|
1088
|
+
el.textContent = `📎 ${filename || part.url || 'file'}`;
|
|
1089
|
+
return el;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
function renderStepMarker(label) {
|
|
1093
|
+
const el = document.createElement('div');
|
|
1094
|
+
el.className = 'ocpv2-part ocpv2-part-step';
|
|
1095
|
+
el.textContent = label;
|
|
1096
|
+
return el;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// A part type this panel has no dedicated view for yet.
|
|
1100
|
+
function renderUnknownPart(part) {
|
|
1101
|
+
return renderNotePart(`[${part.type || 'unknown'} part]`);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// One muted line: subtask / agent / patch markers, retries.
|
|
1105
|
+
function renderNotePart(text, extraClass = '') {
|
|
1106
|
+
if (!text) return null;
|
|
1107
|
+
const el = document.createElement('div');
|
|
1108
|
+
el.className = `ocpv2-part ocpv2-part-note ${extraClass}`.trim();
|
|
1109
|
+
el.textContent = text;
|
|
1110
|
+
return el;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// The error an assistant message ended with (AssistantMessage.error).
|
|
1114
|
+
function renderMessageError(msg, view, store) {
|
|
1115
|
+
const wrap = document.createElement('div');
|
|
1116
|
+
wrap.className = `ocpv2-msg ocpv2-msg-assistant ocpv2-msg-continuation ocpv2-msg-error ocpv2-msg-error-${view.kind}`;
|
|
1117
|
+
wrap.dataset.messageId = msg.id;
|
|
1118
|
+
const row = document.createElement('div');
|
|
1119
|
+
row.className = view.kind === 'aborted' ? 'ocpv2-part ocpv2-part-note' : 'ocpv2-part ocpv2-message-error';
|
|
1120
|
+
const text = document.createElement('span');
|
|
1121
|
+
text.className = 'ocpv2-message-error-text';
|
|
1122
|
+
text.textContent = view.text;
|
|
1123
|
+
row.appendChild(text);
|
|
1124
|
+
if (view.action === 'compact') {
|
|
1125
|
+
row.appendChild(inlineAction('Compact context', async () => {
|
|
1126
|
+
const at = captureBinding(store);
|
|
1127
|
+
if (!at.sessionId) return;
|
|
1128
|
+
const res = await api.compact({
|
|
1129
|
+
sessionId: at.sessionId, cwd: at.cwd || undefined, mcpProfile: at.mcpProfile || undefined, model: at.model || undefined,
|
|
1130
|
+
});
|
|
1131
|
+
if (at.isCurrent() && (res?.error || res?.ok === false)) store.pushError({ message: res?.error || 'Compact failed' });
|
|
1132
|
+
}, store));
|
|
1133
|
+
} else if (view.action === 'settings') {
|
|
1134
|
+
row.appendChild(inlineAction('Open Settings', () => openProviderSettings(), store));
|
|
1135
|
+
}
|
|
1136
|
+
wrap.appendChild(row);
|
|
1137
|
+
return wrap;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function inlineAction(label, onClick, store) {
|
|
1141
|
+
const btn = document.createElement('button');
|
|
1142
|
+
btn.type = 'button';
|
|
1143
|
+
btn.className = 'ocpv2-inline-action';
|
|
1144
|
+
btn.textContent = label;
|
|
1145
|
+
btn.addEventListener('click', async () => {
|
|
1146
|
+
btn.disabled = true;
|
|
1147
|
+
const at = store ? captureBinding(store) : null;
|
|
1148
|
+
try { await onClick(); }
|
|
1149
|
+
catch (err) { if (!at || at.isCurrent()) store?.pushError?.({ message: err?.message || `${label} failed` }); }
|
|
1150
|
+
finally { btn.disabled = false; }
|
|
1151
|
+
});
|
|
1152
|
+
return btn;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// OpenCode is waiting to retry a failed provider call (session.status retry).
|
|
1156
|
+
function renderRetryBanner(view) {
|
|
1157
|
+
const el = document.createElement('div');
|
|
1158
|
+
el.className = 'ocpv2-retry-banner';
|
|
1159
|
+
const title = document.createElement('span');
|
|
1160
|
+
title.className = 'ocpv2-retry-title';
|
|
1161
|
+
title.textContent = view.nextAt
|
|
1162
|
+
? `${view.title} · next try ${new Date(view.nextAt).toLocaleTimeString()}`
|
|
1163
|
+
: view.title;
|
|
1164
|
+
el.appendChild(title);
|
|
1165
|
+
if (view.message) {
|
|
1166
|
+
const msg = document.createElement('span');
|
|
1167
|
+
msg.className = 'ocpv2-retry-message';
|
|
1168
|
+
msg.textContent = view.message;
|
|
1169
|
+
el.appendChild(msg);
|
|
1170
|
+
}
|
|
1171
|
+
if (view.actionLink) {
|
|
1172
|
+
const link = document.createElement('a');
|
|
1173
|
+
link.className = 'ocpv2-inline-action';
|
|
1174
|
+
link.href = view.actionLink;
|
|
1175
|
+
link.target = '_blank';
|
|
1176
|
+
link.rel = 'noopener noreferrer';
|
|
1177
|
+
link.textContent = view.actionLabel || 'Open';
|
|
1178
|
+
el.appendChild(link);
|
|
1179
|
+
}
|
|
1180
|
+
return el;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
// ── Permission card ──────────────────────────────────────────────────────────
|
|
1184
|
+
|
|
1185
|
+
// What the user typed into "Reject with a reason", by request id (like the
|
|
1186
|
+
// question card's selections below): kept for as long as that request is
|
|
1187
|
+
// open, and never dropped to make room. It goes when the request does: this
|
|
1188
|
+
// card's own reply, an answer from anywhere else (`permission.replied`
|
|
1189
|
+
// reaches every page, whatever session is on screen), the session being
|
|
1190
|
+
// deleted, or the server's list of open requests no longer naming it.
|
|
1191
|
+
const _permissionReasons = createCardDrafts();
|
|
1192
|
+
const permissionKeyOf = (perm) => String(perm?.id || perm?.requestID || perm?.permissionID || '');
|
|
1193
|
+
onEvent((eventType, ev) => {
|
|
1194
|
+
if (eventType === 'permission.replied') _permissionReasons.clear(permissionKeyOf(ev));
|
|
1195
|
+
else if (eventType === 'session.deleted') _permissionReasons.forgetSession(ev?.info?.id || ev?.sessionID || ev?.sessionId);
|
|
1196
|
+
});
|
|
1197
|
+
function settlePermissionReasons(state) {
|
|
1198
|
+
if (!state?.sessionId) return;
|
|
1199
|
+
_permissionReasons.settle(state.sessionId, (state.pendingPermissions || []).map(permissionKeyOf));
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
function renderPermissionCard(perm, store) {
|
|
1203
|
+
const reasonKey = permissionKeyOf(perm);
|
|
1204
|
+
// The session the request belongs to (a sub-agent's own, when it is one).
|
|
1205
|
+
const reasonOwner = String(perm?.sessionID || perm?.sessionId || store.getState().sessionId || '');
|
|
1206
|
+
// OpenCode Permission.Request schema mirrors V1: { id, sessionID, permission, patterns[], metadata, tool?:{messageID,callID} }
|
|
1207
|
+
// `perm.permission` may be the type STRING (e.g. "bash") OR a nested object.
|
|
1208
|
+
const inner = (perm.permission && typeof perm.permission === 'object') ? perm.permission : perm;
|
|
1209
|
+
const permType = String(inner.permission || inner.type || perm.tool || '').toLowerCase();
|
|
1210
|
+
const patterns = Array.isArray(inner.patterns)
|
|
1211
|
+
? inner.patterns.filter(Boolean)
|
|
1212
|
+
: (inner.pattern ? [inner.pattern] : []);
|
|
1213
|
+
const metadata = (inner.metadata && typeof inner.metadata === 'object') ? inner.metadata : {};
|
|
1214
|
+
const callID = inner.tool?.callID || inner.tool?.callId || perm.tool?.callID || perm.tool?.callId || '';
|
|
1215
|
+
|
|
1216
|
+
const info = describePermission(permType, patterns, metadata, callID, store);
|
|
1217
|
+
const view = permissionCardView(perm, { sessionId: store.getState().sessionId });
|
|
1218
|
+
|
|
1219
|
+
// Auto-accept is answering this one: a line, not a card.
|
|
1220
|
+
if (view.auto) {
|
|
1221
|
+
return renderNotePart(`Auto-accepting: ${info.kind}${info.target ? ` · ${_truncate(info.target, 120)}` : ''}`);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
const wrap = document.createElement('div');
|
|
1225
|
+
wrap.className = 'ocpv2-permission';
|
|
1226
|
+
|
|
1227
|
+
const head = document.createElement('div');
|
|
1228
|
+
head.className = 'ocpv2-permission-head';
|
|
1229
|
+
const icon = document.createElement('span');
|
|
1230
|
+
icon.className = 'ocpv2-permission-icon';
|
|
1231
|
+
icon.innerHTML = info.icon;
|
|
1232
|
+
const headText = document.createElement('span');
|
|
1233
|
+
headText.className = 'ocpv2-permission-head-text';
|
|
1234
|
+
const kindEl = document.createElement('span');
|
|
1235
|
+
kindEl.className = 'ocpv2-permission-kind';
|
|
1236
|
+
kindEl.textContent = info.kind;
|
|
1237
|
+
const actionEl = document.createElement('span');
|
|
1238
|
+
actionEl.className = 'ocpv2-permission-action';
|
|
1239
|
+
actionEl.textContent = info.action;
|
|
1240
|
+
headText.appendChild(kindEl);
|
|
1241
|
+
headText.appendChild(actionEl);
|
|
1242
|
+
head.appendChild(icon);
|
|
1243
|
+
head.appendChild(headText);
|
|
1244
|
+
if (view.fromSubagent) {
|
|
1245
|
+
const origin = document.createElement('span');
|
|
1246
|
+
origin.className = 'ocpv2-permission-origin';
|
|
1247
|
+
origin.textContent = 'Sub-agent';
|
|
1248
|
+
head.appendChild(origin);
|
|
1249
|
+
}
|
|
1250
|
+
wrap.appendChild(head);
|
|
1251
|
+
|
|
1252
|
+
const body = document.createElement('div');
|
|
1253
|
+
body.className = 'ocpv2-permission-body';
|
|
1254
|
+
|
|
1255
|
+
if (info.target) {
|
|
1256
|
+
const target = document.createElement('div');
|
|
1257
|
+
target.className = 'ocpv2-permission-target';
|
|
1258
|
+
const code = document.createElement('code');
|
|
1259
|
+
code.textContent = info.target;
|
|
1260
|
+
target.appendChild(code);
|
|
1261
|
+
body.appendChild(target);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
if (info.patternsOut && info.patternsOut.length) {
|
|
1265
|
+
const pat = document.createElement('div');
|
|
1266
|
+
pat.className = 'ocpv2-permission-patterns';
|
|
1267
|
+
const label = document.createElement('span');
|
|
1268
|
+
label.className = 'ocpv2-permission-patterns-label';
|
|
1269
|
+
label.textContent = 'Matches';
|
|
1270
|
+
pat.appendChild(label);
|
|
1271
|
+
for (const p of info.patternsOut) {
|
|
1272
|
+
const c = document.createElement('code');
|
|
1273
|
+
c.textContent = p;
|
|
1274
|
+
pat.appendChild(c);
|
|
1275
|
+
}
|
|
1276
|
+
body.appendChild(pat);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
if (info.diff) {
|
|
1280
|
+
const det = document.createElement('details');
|
|
1281
|
+
det.className = 'ocpv2-permission-diff';
|
|
1282
|
+
const sum = document.createElement('summary');
|
|
1283
|
+
sum.textContent = 'Show diff';
|
|
1284
|
+
const pre = document.createElement('pre');
|
|
1285
|
+
pre.textContent = info.diff;
|
|
1286
|
+
det.appendChild(sum);
|
|
1287
|
+
det.appendChild(pre);
|
|
1288
|
+
body.appendChild(det);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const actions = document.createElement('div');
|
|
1292
|
+
actions.className = 'ocpv2-permission-actions';
|
|
1293
|
+
|
|
1294
|
+
const lock = () => wrap.classList.add('ocpv2-perm-locked');
|
|
1295
|
+
const unlock = () => wrap.classList.remove('ocpv2-perm-locked');
|
|
1296
|
+
|
|
1297
|
+
// Lock optimistically, but un-grey on a genuine failure so the card stays
|
|
1298
|
+
// clickable for a retry instead of freezing the turn forever. On success the
|
|
1299
|
+
// SSE permission.replied event clears pendingPermission and removes the card.
|
|
1300
|
+
const respond = async (response, message) => {
|
|
1301
|
+
lock();
|
|
1302
|
+
const ok = await safeReply(perm, response, store, message);
|
|
1303
|
+
if (!ok) unlock();
|
|
1304
|
+
else _permissionReasons.clear(reasonKey);
|
|
1305
|
+
};
|
|
1306
|
+
|
|
1307
|
+
const allowOnce = button('Allow once', 'ocpv2-perm-allow', () => respond('once'));
|
|
1308
|
+
const allowAlways = button('Always allow', 'ocpv2-perm-allow', () => respond('always'));
|
|
1309
|
+
const reject = button('Reject', 'ocpv2-perm-reject', () => respond('reject'));
|
|
1310
|
+
actions.appendChild(allowOnce);
|
|
1311
|
+
actions.appendChild(allowAlways);
|
|
1312
|
+
actions.appendChild(reject);
|
|
1313
|
+
body.appendChild(actions);
|
|
1314
|
+
|
|
1315
|
+
// What "Always allow" will stop asking about.
|
|
1316
|
+
if (view.always.length) {
|
|
1317
|
+
const always = document.createElement('div');
|
|
1318
|
+
always.className = 'ocpv2-permission-always';
|
|
1319
|
+
always.appendChild(document.createTextNode('Always allow covers '));
|
|
1320
|
+
view.always.forEach((pattern, i) => {
|
|
1321
|
+
if (i) always.appendChild(document.createTextNode(', '));
|
|
1322
|
+
const code = document.createElement('code');
|
|
1323
|
+
code.textContent = pattern;
|
|
1324
|
+
always.appendChild(code);
|
|
1325
|
+
});
|
|
1326
|
+
// How long it lasts: OpenCode keeps the answer in the running serve, not in a saved list.
|
|
1327
|
+
always.appendChild(document.createTextNode(` ${ALWAYS_ALLOW_SCOPE}.`));
|
|
1328
|
+
body.appendChild(always);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
const more = document.createElement('div');
|
|
1332
|
+
more.className = 'ocpv2-permission-more';
|
|
1333
|
+
|
|
1334
|
+
// Reject and tell the model why (the reason reaches it as the tool error).
|
|
1335
|
+
if (supports('feature:permission-reply-message')) {
|
|
1336
|
+
const reasonRow = document.createElement('div');
|
|
1337
|
+
reasonRow.className = 'ocpv2-permission-reason';
|
|
1338
|
+
reasonRow.hidden = true;
|
|
1339
|
+
const reasonInput = document.createElement('input');
|
|
1340
|
+
reasonInput.type = 'text';
|
|
1341
|
+
reasonInput.className = 'ocpv2-permission-reason-input';
|
|
1342
|
+
reasonInput.placeholder = 'What should it do instead?';
|
|
1343
|
+
reasonInput.maxLength = 2000;
|
|
1344
|
+
// A reason that was being typed comes back with the card (the card is
|
|
1345
|
+
// rebuilt on every state event and when the panel returns to the session).
|
|
1346
|
+
const typedReason = _permissionReasons.get(reasonKey);
|
|
1347
|
+
if (typedReason) { reasonInput.value = typedReason; reasonRow.hidden = false; }
|
|
1348
|
+
reasonInput.addEventListener('input', () => _permissionReasons.set(reasonKey, reasonInput.value, { sessionId: reasonOwner }));
|
|
1349
|
+
const sendReason = button('Reject', 'ocpv2-perm-reject', () => respond('reject', reasonInput.value));
|
|
1350
|
+
reasonInput.addEventListener('keydown', (event) => {
|
|
1351
|
+
if (event.key === 'Enter') { event.preventDefault(); respond('reject', reasonInput.value); }
|
|
1352
|
+
});
|
|
1353
|
+
reasonRow.appendChild(reasonInput);
|
|
1354
|
+
reasonRow.appendChild(sendReason);
|
|
1355
|
+
more.appendChild(linkButton('Reject with a reason', () => {
|
|
1356
|
+
reasonRow.hidden = !reasonRow.hidden;
|
|
1357
|
+
if (!reasonRow.hidden) reasonInput.focus();
|
|
1358
|
+
}));
|
|
1359
|
+
body.appendChild(more);
|
|
1360
|
+
body.appendChild(reasonRow);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
wrap.appendChild(body);
|
|
1364
|
+
return wrap;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// Build display info for a permission request from OpenCode's native schema.
|
|
1368
|
+
// Mirrors V1 ocp-tabs.js:describePermission. Bash falls back to reading the
|
|
1369
|
+
// command from the matching tool part (by callID) when metadata is empty.
|
|
1370
|
+
function describePermission(permType, patterns, metadata, callID, store) {
|
|
1371
|
+
const firstPattern = patterns[0] || '';
|
|
1372
|
+
const diff = typeof metadata.diff === 'string' ? metadata.diff : '';
|
|
1373
|
+
const fallbackIcon = '<svg viewBox="0 0 24 24"><path d="M12 2L2 7v6c0 5 4 9 10 11 6-2 10-6 10-11V7l-10-5z"/></svg>';
|
|
1374
|
+
const iconMap = {
|
|
1375
|
+
bash: TOOL_ICONS.bash,
|
|
1376
|
+
edit: TOOL_ICONS.edit,
|
|
1377
|
+
write: TOOL_ICONS.write,
|
|
1378
|
+
read: TOOL_ICONS.read,
|
|
1379
|
+
webfetch: TOOL_ICONS.fetch,
|
|
1380
|
+
fetch: TOOL_ICONS.fetch,
|
|
1381
|
+
};
|
|
1382
|
+
const icon = iconMap[permType] || fallbackIcon;
|
|
1383
|
+
|
|
1384
|
+
if (permType === 'bash') {
|
|
1385
|
+
const cmd = readToolCommandFromStore(store, callID) || firstPattern || '';
|
|
1386
|
+
return {
|
|
1387
|
+
icon, kind: 'Run command', action: 'Execute shell command',
|
|
1388
|
+
target: cmd,
|
|
1389
|
+
patternsOut: patterns.length && patterns[0] !== cmd ? patterns : [],
|
|
1390
|
+
diff: '',
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
if (permType === 'edit') {
|
|
1394
|
+
const fp = metadata.filepath || metadata.filePath || metadata.file_path || firstPattern || '';
|
|
1395
|
+
return {
|
|
1396
|
+
icon, kind: 'Edit file',
|
|
1397
|
+
action: patterns.length > 1 ? `Modify ${patterns.length} files` : 'Modify file',
|
|
1398
|
+
target: fp,
|
|
1399
|
+
patternsOut: patterns.length > 1 ? patterns : [],
|
|
1400
|
+
diff,
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
if (permType === 'write') {
|
|
1404
|
+
const fp = metadata.filepath || metadata.filePath || firstPattern || '';
|
|
1405
|
+
return { icon, kind: 'Write file', action: 'Create or overwrite file', target: fp, patternsOut: [], diff };
|
|
1406
|
+
}
|
|
1407
|
+
if (permType === 'read') {
|
|
1408
|
+
const fp = metadata.filepath || metadata.filePath || firstPattern || '';
|
|
1409
|
+
return { icon, kind: 'Read file', action: 'Access file contents', target: fp, patternsOut: [], diff: '' };
|
|
1410
|
+
}
|
|
1411
|
+
if (permType === 'webfetch' || permType === 'fetch') {
|
|
1412
|
+
const url = metadata.url || firstPattern || '';
|
|
1413
|
+
return { icon, kind: 'Fetch URL', action: 'Make a web request', target: url, patternsOut: [], diff: '' };
|
|
1414
|
+
}
|
|
1415
|
+
const kind = permType ? permType.charAt(0).toUpperCase() + permType.slice(1) : 'Tool';
|
|
1416
|
+
return {
|
|
1417
|
+
icon, kind, action: 'Requires your approval',
|
|
1418
|
+
target: firstPattern,
|
|
1419
|
+
patternsOut: patterns.length > 1 ? patterns.slice(1) : [],
|
|
1420
|
+
diff: '',
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// Walk the store's messages to find the tool part whose callID matches and
|
|
1425
|
+
// extract `command` from its input. V2 doesn't memo tool cards via DOM, so we
|
|
1426
|
+
// read straight from state — same source the renderer uses.
|
|
1427
|
+
function readToolCommandFromStore(store, callID) {
|
|
1428
|
+
if (!callID || !store) return '';
|
|
1429
|
+
const s = store.getState();
|
|
1430
|
+
for (const msg of s.messages.values()) {
|
|
1431
|
+
for (const part of msg.parts.values()) {
|
|
1432
|
+
const partCallId = part.callID || part.id || part.toolCallId || part.tool_use_id;
|
|
1433
|
+
if (!partCallId || String(partCallId) !== String(callID)) continue;
|
|
1434
|
+
const input = part.state?.input ?? part.input;
|
|
1435
|
+
if (input && typeof input === 'object') {
|
|
1436
|
+
if (input.command) return String(input.command);
|
|
1437
|
+
}
|
|
1438
|
+
if (typeof input === 'string') return input;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
return '';
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
// ── Question card (AskUser) ─────────────────────────────────────────────────
|
|
1445
|
+
|
|
1446
|
+
// Selection state survives re-renders. The renderer wipes the message list
|
|
1447
|
+
// container on every state event (message.part.updated etc.), so the card's
|
|
1448
|
+
// JS+DOM state would otherwise reset to "0/N" mid-answer. Keyed by req.id.
|
|
1449
|
+
const _questionSelectionCache = new Map(); // requestId → sectionAnswers
|
|
1450
|
+
const _submittingQuestionIds = new Set(); // requestId/toolCallId → reply in flight
|
|
1451
|
+
const _submittedQuestionIds = new Set(); // requestId → reply accepted locally
|
|
1452
|
+
|
|
1453
|
+
function _getOrInitSelection(reqId, total) {
|
|
1454
|
+
let cached = _questionSelectionCache.get(reqId);
|
|
1455
|
+
if (!cached) {
|
|
1456
|
+
cached = Array.from({ length: total }, () => ({ selected: new Set(), custom: null }));
|
|
1457
|
+
_questionSelectionCache.set(reqId, cached);
|
|
1458
|
+
} else if (cached.length < total) {
|
|
1459
|
+
// v1.14.41 streams the question tool's input incrementally — first render
|
|
1460
|
+
// sees 1 question, next render sees 2, etc. Grow the cache instead of
|
|
1461
|
+
// wiping it, otherwise the user's first click on question[0] is lost as
|
|
1462
|
+
// soon as question[1] arrives and the cache resets.
|
|
1463
|
+
while (cached.length < total) cached.push({ selected: new Set(), custom: null });
|
|
1464
|
+
}
|
|
1465
|
+
// Don't shrink — keep stale slots so a transient drop in question count
|
|
1466
|
+
// doesn't lose user input. Cache is cleared on submit/reject.
|
|
1467
|
+
return cached;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
export function clearQuestionSelection(reqId) {
|
|
1471
|
+
if (reqId) _questionSelectionCache.delete(reqId);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
function markQuestionSubmitted(reqId) {
|
|
1475
|
+
if (reqId) _submittedQuestionIds.add(String(reqId));
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
function markQuestionSubmitting(reqId) {
|
|
1479
|
+
if (reqId) _submittingQuestionIds.add(String(reqId));
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
function clearQuestionSubmitting(reqId) {
|
|
1483
|
+
if (reqId) _submittingQuestionIds.delete(String(reqId));
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
function questionRequestIds(req) {
|
|
1487
|
+
return [
|
|
1488
|
+
req?.id,
|
|
1489
|
+
req?.requestID,
|
|
1490
|
+
req?.requestId,
|
|
1491
|
+
req?.tool?.callID,
|
|
1492
|
+
req?.tool?.callId,
|
|
1493
|
+
].filter(Boolean);
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
function markQuestionRequestSubmitted(req) {
|
|
1497
|
+
for (const id of questionRequestIds(req)) markQuestionSubmitted(id);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
function markQuestionRequestSubmitting(req) {
|
|
1501
|
+
for (const id of questionRequestIds(req)) markQuestionSubmitting(id);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
function clearQuestionRequestSubmitting(req) {
|
|
1505
|
+
for (const id of questionRequestIds(req)) clearQuestionSubmitting(id);
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
function isQuestionSubmitted(reqId) {
|
|
1509
|
+
return reqId ? _submittedQuestionIds.has(String(reqId)) : false;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
function isQuestionSubmitting(reqId) {
|
|
1513
|
+
return reqId ? _submittingQuestionIds.has(String(reqId)) : false;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function questionRequestStatusText(req) {
|
|
1517
|
+
const ids = questionRequestIds(req);
|
|
1518
|
+
if (ids.some(isQuestionSubmitted)) return 'Question answer submitted.';
|
|
1519
|
+
if (ids.some(isQuestionSubmitting)) return 'Submitting answer...';
|
|
1520
|
+
return '';
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
function renderQuestionCard(req, store) {
|
|
1524
|
+
const statusText = questionRequestStatusText(req);
|
|
1525
|
+
if (statusText) {
|
|
1526
|
+
const done = document.createElement('div');
|
|
1527
|
+
done.className = 'ocpv2-part ocpv2-part-question-stub';
|
|
1528
|
+
done.textContent = statusText;
|
|
1529
|
+
return done;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
const wrap = document.createElement('div');
|
|
1533
|
+
wrap.className = 'ocpv2-question';
|
|
1534
|
+
wrap.dataset.requestId = req.id;
|
|
1535
|
+
|
|
1536
|
+
const header = document.createElement('div');
|
|
1537
|
+
header.className = 'ocpv2-question-header';
|
|
1538
|
+
header.textContent = 'Question';
|
|
1539
|
+
wrap.appendChild(header);
|
|
1540
|
+
|
|
1541
|
+
const questions = req.questions || [];
|
|
1542
|
+
const total = questions.length;
|
|
1543
|
+
const sectionAnswers = _getOrInitSelection(req.id, total); // persists across re-renders
|
|
1544
|
+
|
|
1545
|
+
// Submit button — disabled until every question has an answer.
|
|
1546
|
+
const actions = document.createElement('div');
|
|
1547
|
+
actions.className = 'ocpv2-question-actions';
|
|
1548
|
+
const submit = document.createElement('button');
|
|
1549
|
+
submit.type = 'button';
|
|
1550
|
+
submit.className = 'ocpv2-question-submit';
|
|
1551
|
+
submit.disabled = true;
|
|
1552
|
+
submit.textContent = `Submit (0/${total})`;
|
|
1553
|
+
actions.appendChild(submit);
|
|
1554
|
+
|
|
1555
|
+
function answeredCount() {
|
|
1556
|
+
let n = 0;
|
|
1557
|
+
for (let i = 0; i < total; i++) {
|
|
1558
|
+
const sa = sectionAnswers[i];
|
|
1559
|
+
if (sa?.custom) { n++; continue; }
|
|
1560
|
+
if (sa?.selected && sa.selected.size > 0) n++;
|
|
1561
|
+
}
|
|
1562
|
+
return n;
|
|
1563
|
+
}
|
|
1564
|
+
function updateSubmitState() {
|
|
1565
|
+
const n = answeredCount();
|
|
1566
|
+
submit.textContent = `Submit (${n}/${total})`;
|
|
1567
|
+
submit.disabled = n < total;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
questions.forEach((q, qIdx) => {
|
|
1571
|
+
const multiple = q.multiple === true || q.multiSelect === true;
|
|
1572
|
+
const section = document.createElement('div');
|
|
1573
|
+
section.className = 'ocpv2-question-section';
|
|
1574
|
+
|
|
1575
|
+
if (q.header) {
|
|
1576
|
+
const tag = document.createElement('div');
|
|
1577
|
+
tag.className = 'ocpv2-question-tag';
|
|
1578
|
+
tag.textContent = q.header;
|
|
1579
|
+
section.appendChild(tag);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
const title = document.createElement('div');
|
|
1583
|
+
title.className = 'ocpv2-question-title';
|
|
1584
|
+
title.textContent = q.question || '';
|
|
1585
|
+
section.appendChild(title);
|
|
1586
|
+
|
|
1587
|
+
if (multiple) {
|
|
1588
|
+
const hint = document.createElement('div');
|
|
1589
|
+
hint.className = 'ocpv2-question-hint';
|
|
1590
|
+
hint.textContent = 'Select all that apply';
|
|
1591
|
+
section.appendChild(hint);
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
const opts = document.createElement('div');
|
|
1595
|
+
opts.className = 'ocpv2-question-options';
|
|
1596
|
+
if (multiple) opts.classList.add('multi');
|
|
1597
|
+
|
|
1598
|
+
let customInput = null;
|
|
1599
|
+
const optionButtons = [];
|
|
1600
|
+
|
|
1601
|
+
(q.options || []).forEach((opt) => {
|
|
1602
|
+
const btn = document.createElement('button');
|
|
1603
|
+
btn.type = 'button';
|
|
1604
|
+
btn.className = 'ocpv2-question-option';
|
|
1605
|
+
const lbl = document.createElement('div');
|
|
1606
|
+
lbl.className = 'ocpv2-question-option-label';
|
|
1607
|
+
lbl.textContent = opt.label;
|
|
1608
|
+
btn.appendChild(lbl);
|
|
1609
|
+
if (opt.description) {
|
|
1610
|
+
const desc = document.createElement('div');
|
|
1611
|
+
desc.className = 'ocpv2-question-option-desc';
|
|
1612
|
+
desc.textContent = opt.description;
|
|
1613
|
+
btn.appendChild(desc);
|
|
1614
|
+
}
|
|
1615
|
+
// Restore prior selection across re-renders.
|
|
1616
|
+
if (sectionAnswers[qIdx].selected.has(opt.label)) btn.classList.add('selected');
|
|
1617
|
+
btn.addEventListener('click', () => {
|
|
1618
|
+
// Selecting an option clears any typed custom answer for this question.
|
|
1619
|
+
if (customInput) customInput.value = '';
|
|
1620
|
+
sectionAnswers[qIdx].custom = null;
|
|
1621
|
+
|
|
1622
|
+
if (multiple) {
|
|
1623
|
+
if (sectionAnswers[qIdx].selected.has(opt.label)) {
|
|
1624
|
+
sectionAnswers[qIdx].selected.delete(opt.label);
|
|
1625
|
+
btn.classList.remove('selected');
|
|
1626
|
+
} else {
|
|
1627
|
+
sectionAnswers[qIdx].selected.add(opt.label);
|
|
1628
|
+
btn.classList.add('selected');
|
|
1629
|
+
}
|
|
1630
|
+
} else {
|
|
1631
|
+
for (const b of optionButtons) b.classList.remove('selected');
|
|
1632
|
+
sectionAnswers[qIdx].selected = new Set([opt.label]);
|
|
1633
|
+
btn.classList.add('selected');
|
|
1634
|
+
}
|
|
1635
|
+
updateSubmitState();
|
|
1636
|
+
});
|
|
1637
|
+
opts.appendChild(btn);
|
|
1638
|
+
optionButtons.push(btn);
|
|
1639
|
+
});
|
|
1640
|
+
section.appendChild(opts);
|
|
1641
|
+
|
|
1642
|
+
if (q.custom) {
|
|
1643
|
+
const customWrap = document.createElement('div');
|
|
1644
|
+
customWrap.className = 'ocpv2-question-custom';
|
|
1645
|
+
customInput = document.createElement('input');
|
|
1646
|
+
customInput.type = 'text';
|
|
1647
|
+
customInput.placeholder = 'Or type your own answer…';
|
|
1648
|
+
customInput.className = 'ocpv2-question-custom-input';
|
|
1649
|
+
// Restore typed answer across re-renders.
|
|
1650
|
+
if (sectionAnswers[qIdx].custom) customInput.value = sectionAnswers[qIdx].custom;
|
|
1651
|
+
customInput.addEventListener('input', () => {
|
|
1652
|
+
const value = customInput.value.trim();
|
|
1653
|
+
if (value) {
|
|
1654
|
+
// Typed answer wins — clear any option selection.
|
|
1655
|
+
for (const b of optionButtons) b.classList.remove('selected');
|
|
1656
|
+
sectionAnswers[qIdx].selected = new Set();
|
|
1657
|
+
sectionAnswers[qIdx].custom = value;
|
|
1658
|
+
} else {
|
|
1659
|
+
sectionAnswers[qIdx].custom = null;
|
|
1660
|
+
}
|
|
1661
|
+
updateSubmitState();
|
|
1662
|
+
});
|
|
1663
|
+
customWrap.appendChild(customInput);
|
|
1664
|
+
section.appendChild(customWrap);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
wrap.appendChild(section);
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1670
|
+
submit.addEventListener('click', async () => {
|
|
1671
|
+
if (submit.disabled) return;
|
|
1672
|
+
await submitAnswers(req, sectionAnswers, store);
|
|
1673
|
+
});
|
|
1674
|
+
wrap.appendChild(actions);
|
|
1675
|
+
updateSubmitState();
|
|
1676
|
+
|
|
1677
|
+
const reject = document.createElement('button');
|
|
1678
|
+
reject.type = 'button';
|
|
1679
|
+
reject.className = 'ocpv2-question-reject';
|
|
1680
|
+
reject.textContent = 'Skip';
|
|
1681
|
+
reject.addEventListener('click', async () => {
|
|
1682
|
+
wrap.classList.add('ocpv2-question-locked');
|
|
1683
|
+
try {
|
|
1684
|
+
// A queued request is rejected; a transcript card has no request
|
|
1685
|
+
// server-side, so the turn waiting on it is stopped. Either way only by
|
|
1686
|
+
// the panel whose session tree the card belongs to (skipQuestion).
|
|
1687
|
+
const result = await skipQuestion(store, api, req, { isDescendant: isDescendantSession });
|
|
1688
|
+
// Another session's question: not this panel's to skip.
|
|
1689
|
+
if (result.foreign) store.pushError({ message: FOREIGN_REQUEST_MESSAGE });
|
|
1690
|
+
// The server refused: the question is still open. The card is usable
|
|
1691
|
+
// again and keeps what was picked or typed in it.
|
|
1692
|
+
else if (result.ok === false && !result.moved) {
|
|
1693
|
+
store.pushError({ message: result.error || 'Question skip failed' });
|
|
1694
|
+
wrap.classList.remove('ocpv2-question-locked');
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
if (!result.moved) clearQuestionSelection(req.id);
|
|
1698
|
+
} catch (err) {
|
|
1699
|
+
console.warn('[ocp-v2-render] question reject failed', err);
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
wrap.appendChild(reject);
|
|
1703
|
+
|
|
1704
|
+
return wrap;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
async function submitAnswers(requestOrId, sectionAnswers, store, instantQIdx, instantLabel) {
|
|
1708
|
+
// Build QuestionAnswer[] = Array<Array<string>>
|
|
1709
|
+
const answers = sectionAnswers.map((sa, idx) => {
|
|
1710
|
+
if (instantQIdx === idx && instantLabel != null) return [instantLabel];
|
|
1711
|
+
if (sa.custom) return [sa.custom];
|
|
1712
|
+
return Array.from(sa.selected);
|
|
1713
|
+
});
|
|
1714
|
+
// A bare id is looked up in the queue: only a request this store holds can
|
|
1715
|
+
// be checked for ownership, and nothing else is answered.
|
|
1716
|
+
const requestId = (typeof requestOrId === 'object' && requestOrId !== null) ? requestOrId.id : requestOrId;
|
|
1717
|
+
const req = (typeof requestOrId === 'object' && requestOrId !== null)
|
|
1718
|
+
? requestOrId
|
|
1719
|
+
: (store.getState().pendingQuestions || []).find((q) => String(q?.id || '') === String(requestId || '')) || null;
|
|
1720
|
+
const card = document.querySelector(`.ocpv2-question[data-request-id="${CSS.escape(String(requestId || ''))}"]`);
|
|
1721
|
+
if (card) card.classList.add('ocpv2-question-locked');
|
|
1722
|
+
markQuestionRequestSubmitting(req);
|
|
1723
|
+
markQuestionSubmitting(requestId);
|
|
1724
|
+
// Ownership, the request id of a transcript card and the reply itself are
|
|
1725
|
+
// decided in ocp-v2-approvals.js (answerQuestion): every card, whatever
|
|
1726
|
+
// built it, is checked against the binding the click happened on, again
|
|
1727
|
+
// after the pending list was read and again right before the reply.
|
|
1728
|
+
let result;
|
|
1729
|
+
try { result = await answerQuestion(store, api, req, answers, { isDescendant: isDescendantSession }); }
|
|
1730
|
+
catch (err) { result = { ok: false, error: err?.message || 'Question reply failed', raw: err }; }
|
|
1731
|
+
if (result.ok) {
|
|
1732
|
+
markQuestionSubmitted(result.replyRequestId);
|
|
1733
|
+
markQuestionRequestSubmitted(req);
|
|
1734
|
+
markQuestionSubmitted(requestId);
|
|
1735
|
+
clearQuestionRequestSubmitting(req);
|
|
1736
|
+
clearQuestionSubmitting(requestId);
|
|
1737
|
+
clearQuestionSelection(requestId);
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
clearQuestionRequestSubmitting(req);
|
|
1741
|
+
clearQuestionSubmitting(requestId);
|
|
1742
|
+
if (card) card.classList.remove('ocpv2-question-locked');
|
|
1743
|
+
// The panel is on another session by now: its transcript gets no banner
|
|
1744
|
+
// about a question that is not its own.
|
|
1745
|
+
if (result.moved) return;
|
|
1746
|
+
console.warn('[ocp-v2-render] question reply failed', result.error);
|
|
1747
|
+
store.pushError({ message: result.error || 'Question reply failed', raw: result.raw });
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
function getQuestionDirectory(store) {
|
|
1751
|
+
const s = store.getState();
|
|
1752
|
+
return s.cwd || s.sessionInfo?.directory || s.sessionInfo?.info?.directory || undefined;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
// The click on a permission card. replyToPermission re-checks that the request
|
|
1756
|
+
// belongs to this panel's session tree before anything is sent.
|
|
1757
|
+
async function safeReply(perm, response, store, message) {
|
|
1758
|
+
const result = await replyToPermission(store, api, perm, response, {
|
|
1759
|
+
cwd: getQuestionDirectory(store),
|
|
1760
|
+
message,
|
|
1761
|
+
isDescendant: isDescendantSession,
|
|
1762
|
+
});
|
|
1763
|
+
if (result.ok) return true;
|
|
1764
|
+
if (result.foreign) store.pushError({ message: FOREIGN_REQUEST_MESSAGE });
|
|
1765
|
+
// `moved`: the panel was rebound while the click was being checked. The card
|
|
1766
|
+
// that was clicked is gone with its binding; nothing was sent.
|
|
1767
|
+
else if (!result.moved) console.warn('[ocp-v2-render] permission reply failed', result.error || '');
|
|
1768
|
+
return false;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
function linkButton(label, onClick) {
|
|
1772
|
+
const b = document.createElement('button');
|
|
1773
|
+
b.type = 'button';
|
|
1774
|
+
b.className = 'ocpv2-permission-link';
|
|
1775
|
+
b.textContent = label;
|
|
1776
|
+
b.addEventListener('click', onClick);
|
|
1777
|
+
return b;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
function button(label, extraClass, onClick) {
|
|
1781
|
+
const b = document.createElement('button');
|
|
1782
|
+
b.className = `ocpv2-permission-btn ${extraClass}`;
|
|
1783
|
+
b.textContent = label;
|
|
1784
|
+
b.addEventListener('click', onClick);
|
|
1785
|
+
return b;
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
// ── Error banner ─────────────────────────────────────────────────────────────
|
|
1789
|
+
|
|
1790
|
+
function renderErrorBanner(err, store) {
|
|
1791
|
+
const el = document.createElement('div');
|
|
1792
|
+
el.className = 'ocpv2-error-banner';
|
|
1793
|
+
const text = document.createElement('span');
|
|
1794
|
+
text.className = 'ocpv2-error-banner-text';
|
|
1795
|
+
text.textContent = err.message || 'Error';
|
|
1796
|
+
el.appendChild(text);
|
|
1797
|
+
// A notice that keeps something for the user (a prompt whose session is
|
|
1798
|
+
// gone) says what can be done with it: `actions` is `[{ label, run(err) }]`.
|
|
1799
|
+
// It goes when one of them is chosen, not through an unnamed ×.
|
|
1800
|
+
const actions = (Array.isArray(err.actions) ? err.actions : []).filter((action) => action?.label && typeof action.run === 'function');
|
|
1801
|
+
for (const action of actions) {
|
|
1802
|
+
const act = document.createElement('button');
|
|
1803
|
+
act.type = 'button';
|
|
1804
|
+
act.className = 'ocpv2-inline-action';
|
|
1805
|
+
act.textContent = action.label;
|
|
1806
|
+
act.addEventListener('click', () => action.run(err));
|
|
1807
|
+
el.appendChild(act);
|
|
1808
|
+
}
|
|
1809
|
+
if (err.id && store?.dismissError && !actions.length) {
|
|
1810
|
+
const close = document.createElement('button');
|
|
1811
|
+
close.type = 'button';
|
|
1812
|
+
close.className = 'ocpv2-error-banner-dismiss';
|
|
1813
|
+
close.setAttribute('aria-label', 'Dismiss');
|
|
1814
|
+
close.setAttribute('data-tooltip', 'Dismiss');
|
|
1815
|
+
close.textContent = '×';
|
|
1816
|
+
close.addEventListener('click', () => store.dismissError(err.id));
|
|
1817
|
+
el.appendChild(close);
|
|
1818
|
+
}
|
|
1819
|
+
return el;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
1823
|
+
|
|
1824
|
+
function formatJson(v) {
|
|
1825
|
+
try {
|
|
1826
|
+
return typeof v === 'string' ? v : JSON.stringify(v, null, 2);
|
|
1827
|
+
} catch {
|
|
1828
|
+
return String(v);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// Markdown rendering. If marked is loaded → full GFM (tables, strikethrough,
|
|
1833
|
+
// blockquotes, headings, hr, lists, code, links), sanitized. If still loading
|
|
1834
|
+
// → escaped text with <br>, and we re-render once marked finishes.
|
|
1835
|
+
// LRU-ish memoization for renderMarkdown. Each render() pass re-builds every
|
|
1836
|
+
// part bubble; without this, marked.parse() runs once per part per state
|
|
1837
|
+
// change — dominant CPU cost on long transcripts.
|
|
1838
|
+
const _markdownCache = new Map();
|
|
1839
|
+
const _markdownCacheLimit = 512;
|
|
1840
|
+
|
|
1841
|
+
function renderMarkdown(src) {
|
|
1842
|
+
const text = String(src || '');
|
|
1843
|
+
const cached = _markdownCache.get(text);
|
|
1844
|
+
if (cached !== undefined) return cached;
|
|
1845
|
+
let html;
|
|
1846
|
+
if (_marked) {
|
|
1847
|
+
// marked passes raw HTML in the source straight through, and the source is
|
|
1848
|
+
// model and tool output: only allowlisted markup may reach innerHTML.
|
|
1849
|
+
try { html = sanitizeHtmlString(_marked.parse(text)); } catch { html = escapeHtml(text).replace(/\n/g, '<br>'); }
|
|
1850
|
+
} else {
|
|
1851
|
+
html = escapeHtml(text).replace(/\n/g, '<br>');
|
|
1852
|
+
}
|
|
1853
|
+
if (_markdownCache.size >= _markdownCacheLimit) {
|
|
1854
|
+
// Evict oldest insertion (Map preserves insertion order)
|
|
1855
|
+
const firstKey = _markdownCache.keys().next().value;
|
|
1856
|
+
if (firstKey !== undefined) _markdownCache.delete(firstKey);
|
|
1857
|
+
}
|
|
1858
|
+
_markdownCache.set(text, html);
|
|
1859
|
+
return html;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
function escapeHtml(s) {
|
|
1863
|
+
return String(s)
|
|
1864
|
+
.replace(/&/g, '&')
|
|
1865
|
+
.replace(/</g, '<')
|
|
1866
|
+
.replace(/>/g, '>')
|
|
1867
|
+
.replace(/"/g, '"')
|
|
1868
|
+
.replace(/'/g, ''');
|
|
1869
|
+
}
|