clawmini 0.0.8 → 0.0.9
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/.changeset/README.md +8 -0
- package/.changeset/config.json +14 -0
- package/.github/workflows/release.yml +49 -0
- package/CHANGELOG.md +36 -0
- package/README.md +5 -4
- package/dist/adapter-discord/index.d.mts.map +1 -1
- package/dist/adapter-discord/index.mjs +465 -282
- package/dist/adapter-discord/index.mjs.map +1 -1
- package/dist/adapter-google-chat/index.mjs +367 -243
- package/dist/adapter-google-chat/index.mjs.map +1 -1
- package/dist/cli/index.mjs +684 -24
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lite.mjs +43 -13
- package/dist/cli/lite.mjs.map +1 -1
- package/dist/cli/{propose-policy.mjs → manage-policies.mjs} +270 -47
- package/dist/cli/manage-policies.mjs.map +1 -0
- package/dist/cli/run-host.d.mts +1 -0
- package/dist/cli/run-host.mjs +3090 -0
- package/dist/cli/run-host.mjs.map +1 -0
- package/dist/config-CPFQIGdG.mjs +57 -0
- package/dist/config-CPFQIGdG.mjs.map +1 -0
- package/dist/config-Dvl-Pov4.mjs +76 -0
- package/dist/config-Dvl-Pov4.mjs.map +1 -0
- package/dist/daemon/index.d.mts.map +1 -1
- package/dist/daemon/index.mjs +970 -332
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/supervisor-actions-CiW56eLi.mjs +843 -0
- package/dist/supervisor-actions-CiW56eLi.mjs.map +1 -0
- package/dist/turn-log-buffer-DRgW53gl.mjs +767 -0
- package/dist/turn-log-buffer-DRgW53gl.mjs.map +1 -0
- package/dist/web/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
- package/dist/web/_app/immutable/chunks/BhRSsUCh.js +2 -0
- package/dist/web/_app/immutable/chunks/BiLeM2i1.js +1 -0
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js → dist/web/_app/immutable/chunks/BmBj85Ll.js} +1 -1
- package/dist/web/_app/immutable/chunks/BrERcKAH.js +1 -0
- package/dist/web/_app/immutable/chunks/Bv9252RM.js +1 -0
- package/dist/web/_app/immutable/chunks/CIXNBPKi.js +1 -0
- package/dist/web/_app/immutable/chunks/DISKL3GN.js +2 -0
- package/dist/web/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
- package/dist/web/_app/immutable/chunks/DnQ3vS13.js +1 -0
- package/dist/web/_app/immutable/chunks/KsloHTKS.js +1 -0
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/Ck-be5J2.js → dist/web/_app/immutable/chunks/RsHsUj-8.js} +2 -2
- package/dist/web/_app/immutable/chunks/{vDehDcuJ.js → wpfV79dV.js} +1 -1
- package/dist/web/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
- package/dist/web/_app/immutable/entry/start.Di0-Jhte.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.CUGC2p-K.js → 0.DYyUA1au.js} +1 -1
- package/dist/web/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
- package/dist/web/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/nodes/3.0arZe_Uf.js → dist/web/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
- package/dist/web/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
- package/dist/web/_app/immutable/nodes/{5.Bq2JzCEj.js → 5.DLbPVJY2.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +12 -12
- package/dist/workspace-oWmVh5mi.mjs +1001 -0
- package/dist/workspace-oWmVh5mi.mjs.map +1 -0
- package/docs/23_adapter_slash_autocomplete/development_log.md +19 -0
- package/docs/23_adapter_slash_autocomplete/notes.md +18 -0
- package/docs/23_adapter_slash_autocomplete/prd.md +46 -0
- package/docs/23_adapter_slash_autocomplete/questions.md +6 -0
- package/docs/23_adapter_slash_autocomplete/tickets.md +21 -0
- package/docs/24_subagent_job_policy_fixes/development_log.md +22 -0
- package/docs/24_subagent_job_policy_fixes/notes.md +28 -0
- package/docs/24_subagent_job_policy_fixes/prd.md +59 -0
- package/docs/24_subagent_job_policy_fixes/questions.md +3 -0
- package/docs/24_subagent_job_policy_fixes/tickets.md +49 -0
- package/docs/25_e2e_test_improvements/development_log.md +30 -0
- package/docs/25_e2e_test_improvements/notes.md +29 -0
- package/docs/25_e2e_test_improvements/prd.md +43 -0
- package/docs/25_e2e_test_improvements/questions.md +12 -0
- package/docs/25_e2e_test_improvements/tickets-2.md +22 -0
- package/docs/25_e2e_test_improvements/tickets.md +22 -0
- package/docs/25_policy_cwd/development_log.md +30 -0
- package/docs/25_policy_cwd/notes.md +28 -0
- package/docs/25_policy_cwd/prd.md +77 -0
- package/docs/25_policy_cwd/questions.md +6 -0
- package/docs/25_policy_cwd/tickets.md +77 -0
- package/docs/CLI_REFERENCE.md +3 -1
- package/docs/PHILOSOPHY.md +35 -0
- package/docs/adapter-visibility/SPEC.md +461 -0
- package/docs/adapter-visibility/SPEC_v2.md +202 -0
- package/docs/auto-update/SPEC.md +344 -0
- package/docs/backups/SPEC.md +296 -0
- package/docs/backups/clawmini.gitignore +69 -0
- package/docs/guides/assets/clawmini-avatar.png +0 -0
- package/docs/guides/backups.md +332 -0
- package/docs/guides/discord_adapter_setup.md +1 -1
- package/docs/guides/google_chat_adapter_setup.md +81 -0
- package/docs/unified-startup/SPEC.md +203 -0
- package/e2e/_helpers/test-environment.test.ts +49 -0
- package/e2e/_helpers/test-environment.ts +548 -0
- package/e2e/adapters/_google-chat-fixtures.ts +340 -0
- package/{src/cli/e2e → e2e/adapters}/adapter-discord.test.ts +22 -23
- package/e2e/adapters/adapter-google-chat-downtime.test.ts +157 -0
- package/e2e/adapters/adapter-google-chat-inbound.test.ts +697 -0
- package/e2e/adapters/adapter-google-chat-outbound.test.ts +297 -0
- package/e2e/adapters/adapter-google-chat-roundtrip.test.ts +56 -0
- package/e2e/adapters/adapter-google-chat-threads.test.ts +1078 -0
- package/e2e/agents/custom-api-env.test.ts +80 -0
- package/e2e/agents/export-lite-func.test.ts +104 -0
- package/e2e/agents/fallbacks.test.ts +124 -0
- package/e2e/agents/interrupt.test.ts +50 -0
- package/e2e/agents/no-reply-necessary.test.ts +57 -0
- package/e2e/agents/session-timeout-subagents.test.ts +76 -0
- package/e2e/agents/subagent-authorization.test.ts +246 -0
- package/e2e/agents/subagent-env.test.ts +49 -0
- package/e2e/agents/subagent-lifecycle.test.ts +782 -0
- package/e2e/agents/subagents-depth.test.ts +47 -0
- package/e2e/cli/agents.test.ts +176 -0
- package/e2e/cli/auto-update.test.ts +741 -0
- package/e2e/cli/basic.test.ts +44 -0
- package/{src/cli/e2e → e2e/cli}/export-lite.test.ts +16 -12
- package/e2e/cli/init-gitignore.test.ts +86 -0
- package/e2e/cli/init.test.ts +76 -0
- package/e2e/cli/messages.test.ts +363 -0
- package/e2e/cli/serve.test.ts +76 -0
- package/{src/cli/e2e → e2e/cli}/skills.test.ts +11 -10
- package/{src/cli/e2e → e2e/daemon}/daemon.test.ts +57 -195
- package/e2e/jobs/agent-jobs.test.ts +216 -0
- package/e2e/jobs/cron.test.ts +64 -0
- package/e2e/jobs/restart.test.ts +108 -0
- package/e2e/policies/approval-session.test.ts +69 -0
- package/e2e/policies/auto-create-policies-file.test.ts +35 -0
- package/e2e/policies/builtin-manage-policies.test.ts +184 -0
- package/e2e/policies/builtin-run-host.test.ts +180 -0
- package/e2e/policies/environment-policies.test.ts +177 -0
- package/e2e/policies/manage-policies.test.ts +566 -0
- package/e2e/policies/output-size.test.ts +98 -0
- package/e2e/policies/policies-context-cwd.test.ts +160 -0
- package/e2e/policies/relative-script-path.test.ts +60 -0
- package/e2e/policies/requests-show.test.ts +135 -0
- package/e2e/policies/requests.test.ts +208 -0
- package/e2e/policies/slash-policies.test.ts +308 -0
- package/e2e/policies/startup-cleanup.test.ts +48 -0
- package/e2e/routers/session-timeout.test.ts +106 -0
- package/e2e/routers/slash-model.test.ts +152 -0
- package/e2e/routers/slash-new.test.ts +50 -0
- package/e2e/routers/slash-restart-adapter.test.ts +96 -0
- package/e2e/routers/slash-restart.test.ts +114 -0
- package/e2e/routers/slash-shutdown.test.ts +55 -0
- package/e2e/routers/slash-stop.test.ts +232 -0
- package/e2e/routers/slash-upgrade.test.ts +88 -0
- package/{src/cli/e2e → e2e/sandbox}/environments.test.ts +14 -13
- package/eslint.config.js +6 -0
- package/napkin.md +1 -1
- package/package.json +8 -3
- package/src/adapter-discord/commands.test.ts +42 -0
- package/src/adapter-discord/commands.ts +33 -0
- package/src/adapter-discord/config.ts +12 -0
- package/src/adapter-discord/forwarder.test.ts +499 -21
- package/src/adapter-discord/forwarder.ts +343 -124
- package/src/adapter-discord/inbound-cache.test.ts +47 -0
- package/src/adapter-discord/inbound-cache.ts +37 -0
- package/src/adapter-discord/index.test.ts +67 -2
- package/src/adapter-discord/index.ts +84 -216
- package/src/adapter-discord/interactions.test.ts +54 -3
- package/src/adapter-discord/interactions.ts +97 -53
- package/src/adapter-discord/processMessage.ts +239 -0
- package/src/adapter-discord/state.ts +1 -0
- package/src/adapter-google-chat/auth.test.ts +9 -5
- package/src/adapter-google-chat/auth.ts +29 -23
- package/src/adapter-google-chat/cards.ts +7 -2
- package/src/adapter-google-chat/client.test.ts +37 -2
- package/src/adapter-google-chat/client.ts +138 -38
- package/src/adapter-google-chat/config.ts +19 -0
- package/src/adapter-google-chat/forwarder.test.ts +81 -56
- package/src/adapter-google-chat/forwarder.ts +394 -185
- package/src/adapter-google-chat/inbound-cache.test.ts +61 -0
- package/src/adapter-google-chat/inbound-cache.ts +36 -0
- package/src/adapter-google-chat/state.test.ts +1 -0
- package/src/adapter-google-chat/state.ts +9 -1
- package/src/adapter-google-chat/subscriptions.ts +8 -6
- package/src/cli/builtin-policies.ts +44 -0
- package/src/cli/commands/agents.ts +59 -5
- package/src/cli/commands/down.ts +54 -2
- package/src/cli/commands/environments.ts +8 -2
- package/src/cli/commands/init.ts +31 -0
- package/src/cli/commands/logs.ts +116 -0
- package/src/cli/commands/policies.ts +6 -4
- package/src/cli/commands/serve.test.ts +67 -0
- package/src/cli/commands/serve.ts +284 -0
- package/src/cli/commands/up.ts +122 -2
- package/src/cli/commands/web-api/agents.ts +3 -2
- package/src/cli/index.ts +4 -0
- package/src/cli/install-detection.test.ts +72 -0
- package/src/cli/install-detection.ts +48 -0
- package/src/cli/lite.ts +54 -22
- package/src/cli/manage-policies-utils.ts +104 -0
- package/src/cli/manage-policies.ts +291 -0
- package/src/cli/run-host.ts +45 -0
- package/src/cli/supervisor-actions.ts +267 -0
- package/src/cli/supervisor-control.test.ts +129 -0
- package/src/cli/supervisor-control.ts +155 -0
- package/src/cli/supervisor-pid.ts +68 -0
- package/src/cli/supervisor.ts +277 -0
- package/src/daemon/agent/agent-context.ts +11 -11
- package/src/daemon/agent/agent-session.ts +8 -1
- package/src/daemon/agent/chat-logger.test.ts +78 -9
- package/src/daemon/agent/chat-logger.ts +25 -5
- package/src/daemon/agent/turn-registry.test.ts +89 -0
- package/src/daemon/agent/turn-registry.ts +94 -0
- package/src/daemon/agent/types.ts +2 -0
- package/src/daemon/api/agent-policy-endpoints.ts +263 -0
- package/src/daemon/api/agent-router.ts +47 -126
- package/src/daemon/api/index.test.ts +1 -0
- package/src/daemon/api/policy-request.test.ts +7 -5
- package/src/daemon/api/router-utils.ts +6 -5
- package/src/daemon/api/subagent-router.ts +110 -74
- package/src/daemon/api/subagent-utils.test.ts +60 -0
- package/src/daemon/api/subagent-utils.ts +113 -87
- package/src/daemon/api/user-router.ts +34 -8
- package/src/daemon/auth.ts +1 -0
- package/src/daemon/cron.test.ts +62 -4
- package/src/daemon/cron.ts +42 -16
- package/src/daemon/events.ts +65 -0
- package/src/daemon/index.ts +24 -1
- package/src/daemon/message-interruption.test.ts +1 -0
- package/src/daemon/message-jobs.test.ts +1 -0
- package/src/daemon/message.ts +78 -14
- package/src/daemon/observation.test.ts +26 -18
- package/src/daemon/pending-replies.test.ts +112 -0
- package/src/daemon/pending-replies.ts +162 -0
- package/src/daemon/policy-request-service.ts +3 -1
- package/src/daemon/policy-utils.test.ts +66 -1
- package/src/daemon/policy-utils.ts +126 -1
- package/src/daemon/request-store.ts +31 -0
- package/src/daemon/routers/session-timeout.ts +4 -0
- package/src/daemon/routers/slash-model.test.ts +344 -0
- package/src/daemon/routers/slash-model.ts +207 -0
- package/src/daemon/routers/slash-policies.test.ts +38 -32
- package/src/daemon/routers/slash-policies.ts +84 -33
- package/src/daemon/routers/slash-restart.test.ts +69 -0
- package/src/daemon/routers/slash-restart.ts +36 -0
- package/src/daemon/routers/slash-shutdown.test.ts +50 -0
- package/src/daemon/routers/slash-shutdown.ts +28 -0
- package/src/daemon/routers/slash-upgrade.test.ts +116 -0
- package/src/daemon/routers/slash-upgrade.ts +76 -0
- package/src/daemon/routers/types.ts +7 -0
- package/src/daemon/routers.ts +16 -0
- package/src/shared/adapters/blockquote.test.ts +28 -0
- package/src/shared/adapters/blockquote.ts +20 -0
- package/src/shared/adapters/filtering.test.ts +224 -10
- package/src/shared/adapters/filtering.ts +95 -7
- package/src/shared/adapters/inbound-cache.test.ts +48 -0
- package/src/shared/adapters/inbound-cache.ts +54 -0
- package/src/shared/adapters/turn-log-buffer.ts +266 -0
- package/src/shared/adapters/turn-log.test.ts +389 -0
- package/src/shared/adapters/turn-log.ts +357 -0
- package/src/shared/agent-utils.ts +12 -5
- package/src/shared/chats.test.ts +4 -0
- package/src/shared/chats.ts +9 -0
- package/src/shared/config.ts +16 -1
- package/src/shared/lite.ts +76 -2
- package/src/shared/policies.ts +26 -0
- package/src/shared/template-manifest.ts +267 -0
- package/src/shared/utils/shell.ts +61 -0
- package/src/shared/version.ts +34 -0
- package/src/shared/workspace.test.ts +217 -0
- package/src/shared/workspace.ts +626 -48
- package/templates/environments/cladding/allowlist-domain.mjs +125 -0
- package/templates/environments/cladding/env.json +21 -1
- package/templates/environments/cladding/run-with-network.mjs +54 -0
- package/templates/environments/macos-proxy/allowlist-domain.mjs +95 -0
- package/templates/environments/macos-proxy/env.json +8 -1
- package/templates/environments/macos-proxy/proxy.mjs +0 -1
- package/templates/gemini/template.json +5 -0
- package/templates/gemini-claw/template.json +13 -0
- package/templates/skills/clawmini-requests/SKILL.md +69 -10
- package/templates/skills/run-host/SKILL.md +51 -0
- package/templates/skills/skill-creator/SKILL.md +4 -3
- package/templates/skills/skill-creator/scripts/validate.sh +52 -0
- package/tsdown.config.ts +10 -1
- package/vitest.config.ts +2 -2
- package/web/.svelte-kit/ambient.d.ts +292 -118
- package/web/.svelte-kit/generated/server/internal.js +1 -1
- package/web/.svelte-kit/output/client/.vite/manifest.json +126 -136
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BhRSsUCh.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BiLeM2i1.js +1 -0
- package/{dist/web/_app/immutable/chunks/CME08kGM.js → web/.svelte-kit/output/client/_app/immutable/chunks/BmBj85Ll.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BrERcKAH.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Bv9252RM.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CIXNBPKi.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DISKL3GN.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DnQ3vS13.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/KsloHTKS.js +1 -0
- package/{dist/web/_app/immutable/chunks/Ck-be5J2.js → web/.svelte-kit/output/client/_app/immutable/chunks/RsHsUj-8.js} +2 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{vDehDcuJ.js → wpfV79dV.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.Di0-Jhte.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{0.CUGC2p-K.js → 0.DYyUA1au.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
- package/{dist/web/_app/immutable/nodes/3.0arZe_Uf.js → web/.svelte-kit/output/client/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.Bq2JzCEj.js → 5.DLbPVJY2.js} +1 -1
- package/web/.svelte-kit/output/client/_app/version.json +1 -1
- package/web/.svelte-kit/output/server/.vite/manifest.json +12 -10
- package/web/.svelte-kit/output/server/chunks/Icon.js +1 -1
- package/web/.svelte-kit/output/server/chunks/client.js +1 -1
- package/web/.svelte-kit/output/server/chunks/exports.js +1 -1
- package/web/.svelte-kit/output/server/chunks/index-server.js +2 -1
- package/web/.svelte-kit/output/server/chunks/internal.js +1 -1
- package/web/.svelte-kit/output/server/chunks/render-context.js +77 -0
- package/web/.svelte-kit/output/server/chunks/root.js +739 -788
- package/web/.svelte-kit/output/server/chunks/shared.js +234 -21
- package/web/.svelte-kit/output/server/index.js +126 -90
- package/web/.svelte-kit/output/server/manifest-full.js +1 -1
- package/web/.svelte-kit/output/server/manifest.js +1 -1
- package/web/.svelte-kit/output/server/nodes/0.js +1 -1
- package/web/.svelte-kit/output/server/nodes/1.js +1 -1
- package/web/.svelte-kit/output/server/nodes/2.js +1 -1
- package/web/.svelte-kit/output/server/nodes/3.js +1 -1
- package/web/.svelte-kit/output/server/nodes/4.js +1 -1
- package/web/.svelte-kit/output/server/nodes/5.js +1 -1
- package/web/.svelte-kit/output/server/remote-entry.js +245 -81
- package/web/.svelte-kit/tsconfig.json +4 -1
- package/dist/cli/propose-policy.mjs.map +0 -1
- package/dist/lite-CBxOT1y5.mjs +0 -241
- package/dist/lite-CBxOT1y5.mjs.map +0 -1
- package/dist/routing-D8rTxtaV.mjs +0 -245
- package/dist/routing-D8rTxtaV.mjs.map +0 -1
- package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +0 -1
- package/dist/web/_app/immutable/chunks/BmRlVmv6.js +0 -1
- package/dist/web/_app/immutable/chunks/CK9JZLaG.js +0 -2
- package/dist/web/_app/immutable/chunks/Ck3rYNON.js +0 -1
- package/dist/web/_app/immutable/chunks/D5iV40bG.js +0 -1
- package/dist/web/_app/immutable/chunks/DMtIqaiV.js +0 -2
- package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
- package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
- package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
- package/dist/web/_app/immutable/entry/app.BCSV3nrG.js +0 -2
- package/dist/web/_app/immutable/entry/start.D4eLEZUM.js +0 -1
- package/dist/web/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
- package/dist/web/_app/immutable/nodes/4.ClM1bXLE.js +0 -60
- package/dist/workspace-BJmJBfKi.mjs +0 -456
- package/dist/workspace-BJmJBfKi.mjs.map +0 -1
- package/src/cli/e2e/agents.test.ts +0 -140
- package/src/cli/e2e/basic.test.ts +0 -43
- package/src/cli/e2e/cron.test.ts +0 -132
- package/src/cli/e2e/export-lite-func.test.ts +0 -206
- package/src/cli/e2e/fallbacks.test.ts +0 -175
- package/src/cli/e2e/init.test.ts +0 -77
- package/src/cli/e2e/messages.test.ts +0 -332
- package/src/cli/e2e/propose-policy.test.ts +0 -203
- package/src/cli/e2e/requests.test.ts +0 -180
- package/src/cli/e2e/session-timeout.test.ts +0 -192
- package/src/cli/e2e/slash-new.test.ts +0 -93
- package/src/cli/e2e/subagents.test.ts +0 -106
- package/src/cli/e2e/utils.ts +0 -66
- package/src/cli/propose-policy.ts +0 -91
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BmRlVmv6.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/D5iV40bG.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.BCSV3nrG.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.D4eLEZUM.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.ClM1bXLE.js +0 -60
- package/web/.svelte-kit/output/server/chunks/false.js +0 -4
- /package/dist/cli/{propose-policy.d.mts → manage-policies.d.mts} +0 -0
- /package/{src/cli/e2e → e2e/_helpers}/global-setup.ts +0 -0
|
@@ -19,13 +19,28 @@ vi.mock('./state.js', () => ({
|
|
|
19
19
|
updateDiscordState: vi.fn().mockResolvedValue(undefined),
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
+
const mockRestPut = vi.fn().mockResolvedValue(true);
|
|
23
|
+
|
|
22
24
|
vi.mock('discord.js', () => {
|
|
23
25
|
return {
|
|
26
|
+
REST: class {
|
|
27
|
+
setToken = vi.fn().mockReturnThis();
|
|
28
|
+
put = mockRestPut;
|
|
29
|
+
},
|
|
30
|
+
Routes: {
|
|
31
|
+
applicationCommands: vi.fn().mockReturnValue('/mock/commands'),
|
|
32
|
+
},
|
|
24
33
|
Client: class {
|
|
25
34
|
constructor() {
|
|
26
35
|
return mockClientInstance;
|
|
27
36
|
}
|
|
28
37
|
},
|
|
38
|
+
SlashCommandBuilder: class {
|
|
39
|
+
setName = vi.fn().mockReturnThis();
|
|
40
|
+
setDescription = vi.fn().mockReturnThis();
|
|
41
|
+
addStringOption = vi.fn().mockReturnThis();
|
|
42
|
+
toJSON = vi.fn().mockReturnValue({ name: 'mocked_command' });
|
|
43
|
+
},
|
|
29
44
|
Events: {
|
|
30
45
|
ClientReady: 'ready',
|
|
31
46
|
MessageCreate: 'messageCreate',
|
|
@@ -79,6 +94,12 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
79
94
|
sendMessage: {
|
|
80
95
|
mutate: vi.fn().mockResolvedValue({ success: true }),
|
|
81
96
|
},
|
|
97
|
+
waitForMessages: {
|
|
98
|
+
subscribe: vi.fn(),
|
|
99
|
+
},
|
|
100
|
+
waitForTyping: {
|
|
101
|
+
subscribe: vi.fn(),
|
|
102
|
+
},
|
|
82
103
|
} as unknown as ReturnType<typeof import('./client.js').getTRPCClient>;
|
|
83
104
|
vi.mocked(getTRPCClient).mockReturnValue(mockTrpc);
|
|
84
105
|
vi.mocked(readDiscordConfig).mockResolvedValue({
|
|
@@ -94,6 +115,36 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
94
115
|
vi.mocked(mockClientInstance.once).mockReturnValue(mockClientInstance);
|
|
95
116
|
});
|
|
96
117
|
|
|
118
|
+
it('should register slash commands on ClientReady', async () => {
|
|
119
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
120
|
+
let readyHandler: ((client: any) => Promise<void>) | undefined;
|
|
121
|
+
vi.mocked(mockClientInstance.once).mockImplementation(
|
|
122
|
+
(event: string, cb: (...args: unknown[]) => void) => {
|
|
123
|
+
if (event === 'ready') {
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
+
readyHandler = cb as any;
|
|
126
|
+
}
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
128
|
+
return mockClientInstance as any;
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const { main } = await import('./index.js');
|
|
133
|
+
await main();
|
|
134
|
+
|
|
135
|
+
expect(readyHandler).toBeDefined();
|
|
136
|
+
|
|
137
|
+
if (readyHandler) {
|
|
138
|
+
await readyHandler({ user: { id: 'bot-id', tag: 'bot#1234' } });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const { slashCommands } = await import('./commands.js');
|
|
142
|
+
|
|
143
|
+
expect(mockRestPut).toHaveBeenCalledWith('/mock/commands', {
|
|
144
|
+
body: slashCommands.map((cmd) => cmd.toJSON()),
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
97
148
|
it('should initialize Discord config and exit if init argument is provided', async () => {
|
|
98
149
|
process.argv = ['node', 'index.js', 'init'];
|
|
99
150
|
const { initDiscordConfig } = await import('./config.js');
|
|
@@ -208,6 +259,7 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
208
259
|
files: undefined,
|
|
209
260
|
adapter: 'discord',
|
|
210
261
|
noWait: true,
|
|
262
|
+
externalRef: 'msg-1',
|
|
211
263
|
},
|
|
212
264
|
});
|
|
213
265
|
expect(mockTrpc.sendMessage.mutate).toHaveBeenNthCalledWith(2, {
|
|
@@ -219,6 +271,7 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
219
271
|
files: undefined,
|
|
220
272
|
adapter: 'discord',
|
|
221
273
|
noWait: true,
|
|
274
|
+
externalRef: 'msg-2',
|
|
222
275
|
},
|
|
223
276
|
});
|
|
224
277
|
});
|
|
@@ -484,7 +537,12 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
484
537
|
await vi.runAllTimersAsync();
|
|
485
538
|
|
|
486
539
|
expect(global.fetch).not.toHaveBeenCalled();
|
|
487
|
-
expect(replyMock).toHaveBeenCalledWith(
|
|
540
|
+
expect(replyMock).toHaveBeenCalledWith(
|
|
541
|
+
expect.objectContaining({
|
|
542
|
+
content: expect.stringContaining('exceeds the size limit'),
|
|
543
|
+
allowedMentions: { parse: [] },
|
|
544
|
+
})
|
|
545
|
+
);
|
|
488
546
|
|
|
489
547
|
expect(mockTrpc.sendMessage.mutate).toHaveBeenCalledWith({
|
|
490
548
|
type: 'send-message',
|
|
@@ -523,6 +581,7 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
523
581
|
|
|
524
582
|
const mockReferencedMessage = {
|
|
525
583
|
content: 'Would anyone like to get dinner Sunday?\nOr maybe lunch?',
|
|
584
|
+
author: { id: 'other-user', username: 'other_user' },
|
|
526
585
|
};
|
|
527
586
|
|
|
528
587
|
if (messageHandler) {
|
|
@@ -545,7 +604,8 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
545
604
|
type: 'send-message',
|
|
546
605
|
client: 'cli',
|
|
547
606
|
data: {
|
|
548
|
-
message:
|
|
607
|
+
message:
|
|
608
|
+
"> **other_user said:**\n> Would anyone like to get dinner Sunday?\n> Or maybe lunch?\n\nYes, I'm in!",
|
|
549
609
|
chatId: 'default',
|
|
550
610
|
files: undefined,
|
|
551
611
|
adapter: 'discord',
|
|
@@ -580,6 +640,7 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
580
640
|
const mockInteraction = {
|
|
581
641
|
isButton: () => false,
|
|
582
642
|
isModalSubmit: () => false,
|
|
643
|
+
isChatInputCommand: () => false,
|
|
583
644
|
};
|
|
584
645
|
if (interactionHandler) await interactionHandler(mockInteraction);
|
|
585
646
|
expect(mockTrpc.sendMessage.mutate).not.toHaveBeenCalled();
|
|
@@ -591,6 +652,7 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
591
652
|
const mockInteraction = {
|
|
592
653
|
isButton: () => true,
|
|
593
654
|
isModalSubmit: () => false,
|
|
655
|
+
isChatInputCommand: () => false,
|
|
594
656
|
isRepliable: () => true,
|
|
595
657
|
user: { id: 'unauth' },
|
|
596
658
|
reply: vi.fn(),
|
|
@@ -607,6 +669,7 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
607
669
|
const mockInteraction = {
|
|
608
670
|
isButton: () => true,
|
|
609
671
|
isModalSubmit: () => false,
|
|
672
|
+
isChatInputCommand: () => false,
|
|
610
673
|
user: { id: 'user-123' },
|
|
611
674
|
customId: 'approve_123',
|
|
612
675
|
update: vi.fn(),
|
|
@@ -634,6 +697,7 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
634
697
|
const mockInteraction = {
|
|
635
698
|
isButton: () => true,
|
|
636
699
|
isModalSubmit: () => false,
|
|
700
|
+
isChatInputCommand: () => false,
|
|
637
701
|
user: { id: 'user-123' },
|
|
638
702
|
customId: 'reject_123',
|
|
639
703
|
showModal: vi.fn(),
|
|
@@ -646,6 +710,7 @@ describe('Discord Adapter Entry Point', () => {
|
|
|
646
710
|
const mockInteraction = {
|
|
647
711
|
isButton: () => false,
|
|
648
712
|
isModalSubmit: () => true,
|
|
713
|
+
isChatInputCommand: () => false,
|
|
649
714
|
isFromMessage: () => true,
|
|
650
715
|
user: { id: 'user-123' },
|
|
651
716
|
customId: 'modal_reject_123',
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { Client, Events, GatewayIntentBits, Partials } from 'discord.js';
|
|
4
|
-
import { readDiscordConfig,
|
|
5
|
-
import { readDiscordState
|
|
3
|
+
import { Client, Events, GatewayIntentBits, Partials, REST, Routes } from 'discord.js';
|
|
4
|
+
import { readDiscordConfig, initDiscordConfig } from './config.js';
|
|
5
|
+
import { readDiscordState } from './state.js';
|
|
6
6
|
import { handleDiscordInteraction } from './interactions.js';
|
|
7
7
|
import { getTRPCClient } from './client.js';
|
|
8
8
|
import { startDaemonToDiscordForwarder } from './forwarder.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
import
|
|
14
|
-
import { handleRoutingCommand, type RoutingTrpcClient } from '../shared/adapters/routing.js';
|
|
9
|
+
import { slashCommands } from './commands.js';
|
|
10
|
+
import { type CommandTrpcClient } from '../shared/adapters/commands.js';
|
|
11
|
+
import { type FilteringConfig } from '../shared/adapters/filtering.js';
|
|
12
|
+
|
|
13
|
+
import { processDiscordMessage } from './processMessage.js';
|
|
15
14
|
|
|
16
15
|
export async function main() {
|
|
17
16
|
const args = process.argv.slice(2);
|
|
@@ -46,238 +45,107 @@ export async function main() {
|
|
|
46
45
|
const state = await readDiscordState();
|
|
47
46
|
const filteringConfig: FilteringConfig = { filters: state.filters };
|
|
48
47
|
|
|
49
|
-
client.once(Events.ClientReady, (readyClient) => {
|
|
48
|
+
client.once(Events.ClientReady, async (readyClient) => {
|
|
50
49
|
console.log(`Ready! Logged in as ${readyClient.user.tag}`);
|
|
51
50
|
|
|
51
|
+
// Workaround: pre-cache the authorized user's DM channel so inbound
|
|
52
|
+
// DMs aren't dropped before they reach the MessageCreate handler.
|
|
53
|
+
// discord.js >= 14.26 can't construct a partial DMChannel from a
|
|
54
|
+
// MESSAGE_CREATE payload (no `type`, no `recipients`), and the
|
|
55
|
+
// ChannelManager silently drops the dispatch when the channel isn't
|
|
56
|
+
// already in cache. Opening the DM here populates the cache so later
|
|
57
|
+
// events short-circuit to the existing entry.
|
|
58
|
+
// See: https://github.com/discordjs/discord.js/issues/11486
|
|
59
|
+
try {
|
|
60
|
+
const user = await readyClient.users.fetch(config.authorizedUserId);
|
|
61
|
+
await user.createDM();
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error(
|
|
64
|
+
`Failed to pre-cache DM channel for authorized user ${config.authorizedUserId}:`,
|
|
65
|
+
err
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const rest = new REST({ version: '10' }).setToken(config.botToken);
|
|
71
|
+
console.log('Started refreshing application (/) commands.');
|
|
72
|
+
await rest.put(Routes.applicationCommands(readyClient.user.id), {
|
|
73
|
+
body: slashCommands.map((cmd) => cmd.toJSON()),
|
|
74
|
+
});
|
|
75
|
+
console.log('Successfully reloaded application (/) commands.');
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Error registering slash commands:', error);
|
|
78
|
+
}
|
|
79
|
+
|
|
52
80
|
// Start forwarding from daemon to Discord
|
|
53
81
|
startDaemonToDiscordForwarder(readyClient, trpc, config.authorizedUserId, {
|
|
54
82
|
chatId: config.chatId,
|
|
55
83
|
config: filteringConfig,
|
|
84
|
+
discordConfig: config,
|
|
56
85
|
}).catch((error) => {
|
|
57
86
|
console.error('Error in daemon-to-discord forwarder:', error);
|
|
58
87
|
});
|
|
59
88
|
});
|
|
60
89
|
|
|
61
90
|
client.on(Events.MessageCreate, async (message) => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const externalContextId = message.channelId;
|
|
67
|
-
const currentState = await readDiscordState();
|
|
68
|
-
const mappedChatId = currentState.channelChatMap?.[externalContextId]?.chatId;
|
|
69
|
-
const isRoutingCommand =
|
|
70
|
-
message.content.startsWith('/chat') || message.content.startsWith('/agent');
|
|
71
|
-
|
|
72
|
-
// Enforce requireMention config for guild messages
|
|
73
|
-
if (message.guild) {
|
|
74
|
-
const channelConfig = currentState.channelChatMap?.[externalContextId];
|
|
75
|
-
const requiresMention =
|
|
76
|
-
channelConfig?.requireMention !== undefined
|
|
77
|
-
? channelConfig.requireMention
|
|
78
|
-
: config.requireMention;
|
|
79
|
-
|
|
80
|
-
if (requiresMention) {
|
|
81
|
-
const isMentioned = message.mentions.has(client.user!.id);
|
|
82
|
-
let isReplyToBot = false;
|
|
91
|
+
let isReplyToBot = false;
|
|
92
|
+
let referenceContent: string | undefined;
|
|
93
|
+
let referenceAuthor: string | undefined;
|
|
83
94
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
if (message.reference && message.reference.messageId) {
|
|
96
|
+
try {
|
|
97
|
+
const referencedMessage = await message.fetchReference();
|
|
98
|
+
isReplyToBot = referencedMessage?.author.id === client.user!.id;
|
|
99
|
+
referenceContent = referencedMessage?.content;
|
|
100
|
+
if (referencedMessage) {
|
|
101
|
+
if (referencedMessage.author.bot) {
|
|
102
|
+
referenceAuthor = 'Assistant';
|
|
103
|
+
} else if (referencedMessage.author.id !== config.authorizedUserId) {
|
|
104
|
+
referenceAuthor = referencedMessage.author.username;
|
|
92
105
|
}
|
|
93
106
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Check if the user is authorized
|
|
102
|
-
if (!isAuthorized(message.author.id, config.authorizedUserId)) {
|
|
103
|
-
console.log(
|
|
104
|
-
`Unauthorized message from ${message.author.tag} (${message.author.id}) ignored.`
|
|
105
|
-
);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
console.log(`Received message from ${message.author.tag}: ${message.content}`);
|
|
110
|
-
|
|
111
|
-
if (isRoutingCommand) {
|
|
112
|
-
const stringChatMap = Object.fromEntries(
|
|
113
|
-
Object.entries(currentState.channelChatMap || {}).map(([k, v]) => [k, v.chatId || ''])
|
|
114
|
-
);
|
|
115
|
-
const routingResult = await handleRoutingCommand(
|
|
116
|
-
message.content,
|
|
117
|
-
externalContextId,
|
|
118
|
-
stringChatMap,
|
|
119
|
-
'discord',
|
|
120
|
-
trpc as unknown as RoutingTrpcClient
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
if (routingResult) {
|
|
124
|
-
if (routingResult.type === 'mapped') {
|
|
125
|
-
await updateDiscordState((latestState) => ({
|
|
126
|
-
channelChatMap: {
|
|
127
|
-
...(latestState.channelChatMap || {}),
|
|
128
|
-
[externalContextId]: {
|
|
129
|
-
...(latestState.channelChatMap?.[externalContextId] || {}),
|
|
130
|
-
chatId: routingResult.newChatId,
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
}));
|
|
134
|
-
}
|
|
135
|
-
await message.reply(routingResult.text);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
let targetChatId = mappedChatId;
|
|
141
|
-
|
|
142
|
-
if (!targetChatId && !isRoutingCommand) {
|
|
143
|
-
const isFirstEverMessage =
|
|
144
|
-
!currentState.channelChatMap ||
|
|
145
|
-
Object.values(currentState.channelChatMap).every((entry) => !entry.chatId);
|
|
146
|
-
|
|
147
|
-
if (isFirstEverMessage) {
|
|
148
|
-
targetChatId = config.chatId || 'default';
|
|
149
|
-
console.log(
|
|
150
|
-
`First contact detected. Automatically mapping channel ${externalContextId} to chat ${targetChatId}.`
|
|
151
|
-
);
|
|
152
|
-
await updateDiscordState((latestState) => ({
|
|
153
|
-
channelChatMap: {
|
|
154
|
-
...(latestState.channelChatMap || {}),
|
|
155
|
-
[externalContextId]: {
|
|
156
|
-
...(latestState.channelChatMap?.[externalContextId] || {}),
|
|
157
|
-
chatId: targetChatId as string,
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
}));
|
|
161
|
-
} else {
|
|
162
|
-
const isDirectMessage = !message.guild;
|
|
163
|
-
const isMentioned = message.mentions.has(client.user!.id);
|
|
164
|
-
const isSlashCommand = message.content.startsWith('/');
|
|
165
|
-
if (isDirectMessage || isMentioned || isSlashCommand) {
|
|
166
|
-
console.log(`Unmapped channel ${externalContextId}, sending first contact warning.`);
|
|
167
|
-
await message.reply(
|
|
168
|
-
'This channel/space is not currently mapped to a daemon chat. Please use `/chat [chat-id]` or `/agent [agent-id]` to map it.'
|
|
169
|
-
);
|
|
170
|
-
} else {
|
|
171
|
-
console.log(
|
|
172
|
-
`Unmapped channel ${externalContextId}, silently ignoring background message.`
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
return;
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error('Failed to fetch referenced message for mention check:', err);
|
|
176
109
|
}
|
|
177
110
|
}
|
|
178
111
|
|
|
179
|
-
|
|
180
|
-
|
|
112
|
+
const attachments = message.attachments
|
|
113
|
+
? Array.from(message.attachments.values()).map((att) => ({
|
|
114
|
+
name: att.name,
|
|
115
|
+
size: att.size,
|
|
116
|
+
url: att.url,
|
|
117
|
+
}))
|
|
118
|
+
: [];
|
|
181
119
|
|
|
182
|
-
|
|
120
|
+
await processDiscordMessage(
|
|
183
121
|
message.content,
|
|
122
|
+
message.author,
|
|
123
|
+
message.channelId,
|
|
124
|
+
message.guild,
|
|
125
|
+
async (text) => {
|
|
126
|
+
await message.reply({ content: text, allowedMentions: { parse: [] } });
|
|
127
|
+
},
|
|
128
|
+
config,
|
|
129
|
+
trpc,
|
|
184
130
|
filteringConfig,
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
filteringConfig.filters = commandResult.newConfig.filters;
|
|
193
|
-
await updateDiscordState({ filters: filteringConfig.filters });
|
|
194
|
-
}
|
|
195
|
-
await message.reply(commandResult.text);
|
|
196
|
-
} else if (commandResult.type === 'debug') {
|
|
197
|
-
const formatted =
|
|
198
|
-
commandResult.messages.length === 0
|
|
199
|
-
? 'No ignored background messages found.'
|
|
200
|
-
: `**Debug Output (${commandResult.messages.length} ignored messages):**\n\n` +
|
|
201
|
-
commandResult.messages.map((msg) => formatMessage(msg)).join('\n\n---\n\n');
|
|
202
|
-
await message.reply(formatted);
|
|
131
|
+
{
|
|
132
|
+
mentionsBot: !!message.mentions?.has(client.user!.id),
|
|
133
|
+
isReplyToBot,
|
|
134
|
+
attachments,
|
|
135
|
+
messageId: message.id,
|
|
136
|
+
...(referenceContent ? { referenceContent } : {}),
|
|
137
|
+
...(referenceAuthor ? { referenceAuthor } : {}),
|
|
203
138
|
}
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const downloadedFiles: string[] = [];
|
|
208
|
-
if (message.attachments.size > 0) {
|
|
209
|
-
const tmpDir = path.join(getClawminiDir(process.cwd()), 'tmp', 'discord');
|
|
210
|
-
await fs.mkdir(tmpDir, { recursive: true });
|
|
211
|
-
const maxSizeMB = config.maxAttachmentSizeMB ?? 25;
|
|
212
|
-
const maxSizeBytes = maxSizeMB * 1024 * 1024;
|
|
213
|
-
|
|
214
|
-
for (const attachment of message.attachments.values()) {
|
|
215
|
-
if (attachment.size > maxSizeBytes) {
|
|
216
|
-
console.warn(
|
|
217
|
-
`Attachment ${attachment.name} exceeds size limit (${maxSizeMB}MB). Ignoring.`
|
|
218
|
-
);
|
|
219
|
-
await message.reply(
|
|
220
|
-
`Warning: Attachment ${attachment.name} exceeds the size limit of ${maxSizeMB}MB and was ignored.`
|
|
221
|
-
);
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
const res = await fetch(attachment.url);
|
|
227
|
-
if (!res.ok) {
|
|
228
|
-
console.error(`Failed to download attachment ${attachment.name}`);
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const uniqueName = `${Date.now()}-${attachment.name}`;
|
|
233
|
-
const filePath = path.join(tmpDir, uniqueName);
|
|
234
|
-
const arrayBuffer = await res.arrayBuffer();
|
|
235
|
-
await fs.writeFile(filePath, Buffer.from(arrayBuffer));
|
|
236
|
-
downloadedFiles.push(filePath);
|
|
237
|
-
} catch (err) {
|
|
238
|
-
console.error(`Error downloading attachment ${attachment.name}:`, err);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
let finalContent = message.content;
|
|
244
|
-
|
|
245
|
-
if (message.reference && message.reference.messageId) {
|
|
246
|
-
try {
|
|
247
|
-
const referencedMessage = await message.fetchReference();
|
|
248
|
-
if (referencedMessage && referencedMessage.content) {
|
|
249
|
-
const quotedContent = referencedMessage.content
|
|
250
|
-
.split('\n')
|
|
251
|
-
.map((line) => `> ${line}`)
|
|
252
|
-
.join('\n');
|
|
253
|
-
finalContent = `${quotedContent}\n${finalContent}`;
|
|
254
|
-
}
|
|
255
|
-
} catch (err) {
|
|
256
|
-
console.error('Failed to fetch referenced message:', err);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
console.log(`Forwarding message to daemon: ${finalContent}`);
|
|
261
|
-
try {
|
|
262
|
-
await trpc.sendMessage.mutate({
|
|
263
|
-
type: 'send-message',
|
|
264
|
-
client: 'cli',
|
|
265
|
-
data: {
|
|
266
|
-
message: finalContent,
|
|
267
|
-
chatId: targetChatId,
|
|
268
|
-
files: downloadedFiles.length > 0 ? downloadedFiles : undefined,
|
|
269
|
-
adapter: 'discord',
|
|
270
|
-
noWait: true,
|
|
271
|
-
},
|
|
272
|
-
});
|
|
273
|
-
console.log('Message forwarded to daemon successfully.');
|
|
274
|
-
} catch (error) {
|
|
275
|
-
console.error('Failed to forward message to daemon:', error);
|
|
276
|
-
}
|
|
139
|
+
);
|
|
277
140
|
});
|
|
278
141
|
|
|
279
142
|
client.on(Events.InteractionCreate, async (interaction) => {
|
|
280
|
-
await handleDiscordInteraction(
|
|
143
|
+
await handleDiscordInteraction(
|
|
144
|
+
interaction,
|
|
145
|
+
config,
|
|
146
|
+
trpc as unknown as CommandTrpcClient,
|
|
147
|
+
filteringConfig
|
|
148
|
+
);
|
|
281
149
|
});
|
|
282
150
|
|
|
283
151
|
try {
|
|
@@ -22,11 +22,16 @@ describe('handleDiscordInteraction', () => {
|
|
|
22
22
|
mockInteraction = {
|
|
23
23
|
isButton: vi.fn().mockReturnValue(true),
|
|
24
24
|
isModalSubmit: vi.fn().mockReturnValue(false),
|
|
25
|
+
isChatInputCommand: vi.fn().mockReturnValue(false),
|
|
26
|
+
isRepliable: vi.fn().mockReturnValue(true),
|
|
25
27
|
user: { id: 'user-1' },
|
|
26
28
|
customId: '',
|
|
27
29
|
channelId: 'channel-1',
|
|
28
30
|
update: vi.fn().mockResolvedValue({}),
|
|
29
31
|
followUp: vi.fn().mockResolvedValue({}),
|
|
32
|
+
reply: vi.fn().mockResolvedValue({}),
|
|
33
|
+
deferReply: vi.fn().mockResolvedValue({}),
|
|
34
|
+
deleteReply: vi.fn().mockResolvedValue({}),
|
|
30
35
|
showModal: vi.fn().mockResolvedValue({}),
|
|
31
36
|
};
|
|
32
37
|
vi.mocked(readDiscordState).mockResolvedValue({});
|
|
@@ -43,7 +48,7 @@ describe('handleDiscordInteraction', () => {
|
|
|
43
48
|
|
|
44
49
|
it('routes approve to explicit chat if provided', async () => {
|
|
45
50
|
mockInteraction.customId = 'approve|policy-1|explicit-chat';
|
|
46
|
-
await handleDiscordInteraction(mockInteraction, config, mockTrpc);
|
|
51
|
+
await handleDiscordInteraction(mockInteraction, config, mockTrpc, { filters: {} });
|
|
47
52
|
|
|
48
53
|
expect(mockTrpc.sendMessage.mutate).toHaveBeenCalledWith(
|
|
49
54
|
expect.objectContaining({
|
|
@@ -59,7 +64,7 @@ describe('handleDiscordInteraction', () => {
|
|
|
59
64
|
vi.mocked(readDiscordState).mockResolvedValue({
|
|
60
65
|
channelChatMap: { 'channel-1': { chatId: 'mapped-chat' } },
|
|
61
66
|
});
|
|
62
|
-
await handleDiscordInteraction(mockInteraction, config, mockTrpc);
|
|
67
|
+
await handleDiscordInteraction(mockInteraction, config, mockTrpc, { filters: {} });
|
|
63
68
|
|
|
64
69
|
expect(mockTrpc.sendMessage.mutate).toHaveBeenCalledWith(
|
|
65
70
|
expect.objectContaining({
|
|
@@ -83,7 +88,7 @@ describe('handleDiscordInteraction', () => {
|
|
|
83
88
|
channelChatMap: { 'channel-1': { chatId: 'mapped-chat' } },
|
|
84
89
|
});
|
|
85
90
|
|
|
86
|
-
await handleDiscordInteraction(mockInteraction, config, mockTrpc);
|
|
91
|
+
await handleDiscordInteraction(mockInteraction, config, mockTrpc, { filters: {} });
|
|
87
92
|
|
|
88
93
|
expect(mockTrpc.sendMessage.mutate).toHaveBeenCalledWith(
|
|
89
94
|
expect.objectContaining({
|
|
@@ -93,4 +98,50 @@ describe('handleDiscordInteraction', () => {
|
|
|
93
98
|
})
|
|
94
99
|
);
|
|
95
100
|
});
|
|
101
|
+
|
|
102
|
+
describe('chat input commands', () => {
|
|
103
|
+
beforeEach(() => {
|
|
104
|
+
mockInteraction.isButton.mockReturnValue(false);
|
|
105
|
+
mockInteraction.isModalSubmit.mockReturnValue(false);
|
|
106
|
+
mockInteraction.isChatInputCommand.mockReturnValue(true);
|
|
107
|
+
mockInteraction.options = {
|
|
108
|
+
getString: vi.fn(),
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('routes basic commands like /pending', async () => {
|
|
113
|
+
mockInteraction.commandName = 'pending';
|
|
114
|
+
|
|
115
|
+
await handleDiscordInteraction(mockInteraction, config, mockTrpc, { filters: {} });
|
|
116
|
+
|
|
117
|
+
expect(mockInteraction.deferReply).toHaveBeenCalledWith({ ephemeral: true });
|
|
118
|
+
expect(mockTrpc.sendMessage.mutate).toHaveBeenCalledWith(
|
|
119
|
+
expect.objectContaining({
|
|
120
|
+
data: expect.objectContaining({
|
|
121
|
+
message: '/pending',
|
|
122
|
+
}),
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('routes commands with arguments like /reject', async () => {
|
|
128
|
+
mockInteraction.commandName = 'reject';
|
|
129
|
+
mockInteraction.options.getString.mockImplementation((name: string) => {
|
|
130
|
+
if (name === 'policy_id') return 'req-123';
|
|
131
|
+
if (name === 'rationale') return 'too risky';
|
|
132
|
+
return null;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
await handleDiscordInteraction(mockInteraction, config, mockTrpc, { filters: {} });
|
|
136
|
+
|
|
137
|
+
expect(mockInteraction.deferReply).toHaveBeenCalledWith({ ephemeral: true });
|
|
138
|
+
expect(mockTrpc.sendMessage.mutate).toHaveBeenCalledWith(
|
|
139
|
+
expect.objectContaining({
|
|
140
|
+
data: expect.objectContaining({
|
|
141
|
+
message: '/reject req-123 too risky',
|
|
142
|
+
}),
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
96
147
|
});
|