@openparachute/agent 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.parachute/module.json +124 -8
- package/LICENSE +2 -16
- package/README.md +118 -166
- package/package.json +32 -43
- package/scripts/spawn-agent.ts +371 -0
- package/src/_parked/interactive-spawn.test.ts +324 -0
- package/src/_parked/interactive-spawn.ts +701 -0
- package/src/agent-defs.test.ts +1504 -0
- package/src/agent-defs.ts +1702 -0
- package/src/agent-mcp-config.test.ts +115 -0
- package/src/agent-mcp-config.ts +115 -0
- package/src/agents.test.ts +360 -0
- package/src/agents.ts +379 -0
- package/src/auth.test.ts +46 -0
- package/src/auth.ts +140 -0
- package/src/backends/attached-queue.test.ts +376 -0
- package/src/backends/attached-queue.ts +372 -0
- package/src/backends/programmatic.test.ts +1715 -0
- package/src/backends/programmatic.ts +927 -0
- package/src/backends/registry.test.ts +1494 -0
- package/src/backends/registry.ts +1202 -0
- package/src/backends/stream-json.test.ts +570 -0
- package/src/backends/stream-json.ts +392 -0
- package/src/backends/types.ts +223 -0
- package/src/bridge.ts +417 -0
- package/src/channel-backend-wiring.test.ts +237 -0
- package/src/credentials.test.ts +274 -0
- package/src/credentials.ts +380 -0
- package/src/cron.test.ts +342 -0
- package/src/cron.ts +380 -0
- package/src/daemon-agent-def-api.test.ts +166 -0
- package/src/daemon-agent-defs-api.test.ts +953 -0
- package/src/daemon-agent-env-api.test.ts +338 -0
- package/src/daemon-attached-queue-store.test.ts +65 -0
- package/src/daemon-config-api.test.ts +962 -0
- package/src/daemon-jobs-api.test.ts +271 -0
- package/src/daemon-vault-chat.test.ts +250 -0
- package/src/daemon.test.ts +746 -0
- package/src/daemon.ts +3314 -0
- package/src/def-vaults.test.ts +136 -0
- package/src/def-vaults.ts +165 -0
- package/src/delivery-state.test.ts +110 -0
- package/src/delivery-state.ts +154 -0
- package/src/effective-env.test.ts +114 -0
- package/src/effective-env.ts +184 -0
- package/src/env-compat.ts +39 -0
- package/src/grants.test.ts +638 -0
- package/src/grants.ts +675 -0
- package/src/hub-jwt.test.ts +161 -0
- package/src/hub-jwt.ts +182 -0
- package/src/jobs.test.ts +245 -0
- package/src/jobs.ts +266 -0
- package/src/mcp-http.test.ts +265 -0
- package/src/mcp-http.ts +771 -0
- package/src/mint-token.test.ts +152 -0
- package/src/mint-token.ts +139 -0
- package/src/module-manifest.test.ts +158 -0
- package/src/oauth-discovery.ts +134 -0
- package/src/programmatic-wiring.test.ts +838 -0
- package/src/registry.test.ts +227 -0
- package/src/registry.ts +228 -0
- package/src/resolve-port.test.ts +64 -0
- package/src/routing.test.ts +184 -0
- package/src/routing.ts +76 -0
- package/src/runner.test.ts +506 -0
- package/src/runner.ts +255 -0
- package/src/sandbox/config.test.ts +150 -0
- package/src/sandbox/config.ts +102 -0
- package/src/sandbox/egress.test.ts +113 -0
- package/src/sandbox/egress.ts +123 -0
- package/src/sandbox/index.ts +180 -0
- package/src/sandbox/live-seatbelt.test.ts +277 -0
- package/src/sandbox/mounts.test.ts +154 -0
- package/src/sandbox/mounts.ts +133 -0
- package/src/sandbox/sandbox.test.ts +168 -0
- package/src/sandbox/types.ts +382 -0
- package/src/services-manifest.test.ts +106 -0
- package/src/services-manifest.ts +95 -0
- package/src/spa-serve.test.ts +116 -0
- package/src/spa-serve.ts +116 -0
- package/src/spawn-agent-cli.test.ts +172 -0
- package/src/spawn-agent.test.ts +1218 -0
- package/src/spawn-agent.ts +569 -0
- package/src/spawn-deps.test.ts +54 -0
- package/src/spawn-deps.ts +166 -0
- package/src/telegram/api.ts +153 -0
- package/src/terminal-assets.test.ts +50 -0
- package/src/terminal-assets.ts +79 -0
- package/src/terminal-ui.ts +305 -0
- package/src/terminal.test.ts +530 -0
- package/src/terminal.ts +458 -0
- package/src/transport.ts +270 -0
- package/src/transports/http-ui.test.ts +455 -0
- package/src/transports/http-ui.ts +201 -0
- package/src/transports/telegram.test.ts +174 -0
- package/src/transports/telegram.ts +426 -0
- package/src/transports/vault.test.ts +2011 -0
- package/src/transports/vault.ts +1790 -0
- package/src/ui-kit.test.ts +178 -0
- package/src/ui-kit.ts +402 -0
- package/tsconfig.json +8 -14
- package/web/ui/tsconfig.json +2 -1
- package/.claude/scheduled_tasks.lock +0 -1
- package/.claude/settings.json +0 -5
- package/.claude/skills/add-atomic-chat-tool/SKILL.md +0 -243
- package/.claude/skills/add-atomic-chat-tool/atomic-chat-mcp-stdio.ts +0 -229
- package/.claude/skills/add-codex/SKILL.md +0 -161
- package/.claude/skills/add-dashboard/SKILL.md +0 -138
- package/.claude/skills/add-dashboard/resources/dashboard-pusher.ts +0 -495
- package/.claude/skills/add-emacs/SKILL.md +0 -296
- package/.claude/skills/add-gcal-tool/SKILL.md +0 -210
- package/.claude/skills/add-gchat/REMOVE.md +0 -6
- package/.claude/skills/add-gchat/SKILL.md +0 -92
- package/.claude/skills/add-gchat/VERIFY.md +0 -3
- package/.claude/skills/add-github/REMOVE.md +0 -6
- package/.claude/skills/add-github/SKILL.md +0 -148
- package/.claude/skills/add-github/VERIFY.md +0 -3
- package/.claude/skills/add-gmail-tool/SKILL.md +0 -229
- package/.claude/skills/add-imessage/REMOVE.md +0 -6
- package/.claude/skills/add-imessage/SKILL.md +0 -113
- package/.claude/skills/add-imessage/VERIFY.md +0 -3
- package/.claude/skills/add-karpathy-llm-wiki/SKILL.md +0 -110
- package/.claude/skills/add-karpathy-llm-wiki/llm-wiki.md +0 -75
- package/.claude/skills/add-linear/REMOVE.md +0 -6
- package/.claude/skills/add-linear/SKILL.md +0 -168
- package/.claude/skills/add-linear/VERIFY.md +0 -3
- package/.claude/skills/add-macos-statusbar/SKILL.md +0 -133
- package/.claude/skills/add-macos-statusbar/add/src/statusbar.swift +0 -147
- package/.claude/skills/add-matrix/REMOVE.md +0 -6
- package/.claude/skills/add-matrix/SKILL.md +0 -148
- package/.claude/skills/add-matrix/VERIFY.md +0 -3
- package/.claude/skills/add-ollama-provider/SKILL.md +0 -179
- package/.claude/skills/add-ollama-tool/SKILL.md +0 -193
- package/.claude/skills/add-opencode/SKILL.md +0 -229
- package/.claude/skills/add-parallel/SKILL.md +0 -290
- package/.claude/skills/add-resend/REMOVE.md +0 -6
- package/.claude/skills/add-resend/SKILL.md +0 -93
- package/.claude/skills/add-resend/VERIFY.md +0 -3
- package/.claude/skills/add-signal/REMOVE.md +0 -13
- package/.claude/skills/add-signal/SKILL.md +0 -318
- package/.claude/skills/add-signal/VERIFY.md +0 -5
- package/.claude/skills/add-slack/REMOVE.md +0 -6
- package/.claude/skills/add-slack/SKILL.md +0 -112
- package/.claude/skills/add-slack/VERIFY.md +0 -3
- package/.claude/skills/add-teams/REMOVE.md +0 -6
- package/.claude/skills/add-teams/SKILL.md +0 -207
- package/.claude/skills/add-teams/VERIFY.md +0 -3
- package/.claude/skills/add-vercel/SKILL.md +0 -147
- package/.claude/skills/add-vercel/container-skills/vercel-cli/SKILL.md +0 -103
- package/.claude/skills/add-webex/REMOVE.md +0 -6
- package/.claude/skills/add-webex/SKILL.md +0 -88
- package/.claude/skills/add-webex/VERIFY.md +0 -3
- package/.claude/skills/add-wechat/REMOVE.md +0 -49
- package/.claude/skills/add-wechat/SKILL.md +0 -170
- package/.claude/skills/add-wechat/scripts/wire-dm.ts +0 -172
- package/.claude/skills/add-whatsapp/SKILL.md +0 -264
- package/.claude/skills/add-whatsapp-cloud/REMOVE.md +0 -6
- package/.claude/skills/add-whatsapp-cloud/SKILL.md +0 -95
- package/.claude/skills/add-whatsapp-cloud/VERIFY.md +0 -3
- package/.claude/skills/claw/SKILL.md +0 -131
- package/.claude/skills/claw/scripts/claw +0 -374
- package/.claude/skills/convert-to-apple-container/SKILL.md +0 -212
- package/.claude/skills/customize/SKILL.md +0 -110
- package/.claude/skills/debug/SKILL.md +0 -349
- package/.claude/skills/get-qodo-rules/SKILL.md +0 -122
- package/.claude/skills/get-qodo-rules/references/output-format.md +0 -41
- package/.claude/skills/get-qodo-rules/references/pagination.md +0 -33
- package/.claude/skills/get-qodo-rules/references/repository-scope.md +0 -26
- package/.claude/skills/init-first-agent/SKILL.md +0 -120
- package/.claude/skills/init-onecli/SKILL.md +0 -270
- package/.claude/skills/manage-channels/SKILL.md +0 -87
- package/.claude/skills/manage-mounts/SKILL.md +0 -47
- package/.claude/skills/migrate-from-openclaw/MIGRATE_CRONS.md +0 -100
- package/.claude/skills/migrate-from-openclaw/SKILL.md +0 -447
- package/.claude/skills/migrate-from-openclaw/scripts/discover-openclaw.ts +0 -734
- package/.claude/skills/migrate-from-openclaw/scripts/extract-channel-credentials.ts +0 -476
- package/.claude/skills/migrate-nanoclaw/SKILL.md +0 -484
- package/.claude/skills/migrate-nanoclaw/diagnostics.md +0 -51
- package/.claude/skills/qodo-pr-resolver/SKILL.md +0 -326
- package/.claude/skills/qodo-pr-resolver/resources/providers.md +0 -329
- package/.claude/skills/update-nanoclaw/SKILL.md +0 -243
- package/.claude/skills/update-nanoclaw/diagnostics.md +0 -48
- package/.claude/skills/update-skills/SKILL.md +0 -130
- package/.claude/skills/use-native-credential-proxy/SKILL.md +0 -167
- package/.claude/skills/x-integration/SKILL.md +0 -417
- package/.claude/skills/x-integration/agent.ts +0 -243
- package/.claude/skills/x-integration/host.ts +0 -155
- package/.claude/skills/x-integration/lib/browser.ts +0 -148
- package/.claude/skills/x-integration/lib/config.ts +0 -62
- package/.claude/skills/x-integration/scripts/like.ts +0 -56
- package/.claude/skills/x-integration/scripts/post.ts +0 -66
- package/.claude/skills/x-integration/scripts/quote.ts +0 -80
- package/.claude/skills/x-integration/scripts/reply.ts +0 -74
- package/.claude/skills/x-integration/scripts/retweet.ts +0 -62
- package/.claude/skills/x-integration/scripts/setup.ts +0 -87
- package/.github/CODEOWNERS +0 -10
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -18
- package/.github/workflows/bump-version.yml +0 -35
- package/.github/workflows/ci.yml +0 -39
- package/.github/workflows/label-pr.yml +0 -40
- package/.github/workflows/update-tokens.yml +0 -43
- package/.husky/pre-commit +0 -1
- package/.mcp.json +0 -3
- package/.nvmrc +0 -1
- package/.prettierrc +0 -4
- package/CHANGELOG.md +0 -263
- package/CLAUDE.md +0 -307
- package/CODE_OF_CONDUCT.md +0 -128
- package/CONTRIBUTING.md +0 -159
- package/CONTRIBUTORS.md +0 -26
- package/LICENSE-NANOCLAW-MIT +0 -21
- package/README_ja.md +0 -194
- package/README_zh.md +0 -194
- package/assets/nanoclaw-favicon.png +0 -0
- package/assets/nanoclaw-icon.png +0 -0
- package/assets/nanoclaw-logo-dark.png +0 -0
- package/assets/nanoclaw-logo.png +0 -0
- package/assets/nanoclaw-profile.jpeg +0 -0
- package/assets/nanoclaw-sales.png +0 -0
- package/assets/social-preview.jpg +0 -0
- package/config-examples/mount-allowlist.json +0 -25
- package/container/.dockerignore +0 -2
- package/container/CLAUDE.md +0 -21
- package/container/Dockerfile +0 -121
- package/container/agent-runner/bun.lock +0 -243
- package/container/agent-runner/package.json +0 -22
- package/container/agent-runner/scripts/sdk-signal-probe.ts +0 -169
- package/container/agent-runner/src/config.ts +0 -55
- package/container/agent-runner/src/db/connection.ts +0 -267
- package/container/agent-runner/src/db/index.ts +0 -20
- package/container/agent-runner/src/db/messages-in.ts +0 -138
- package/container/agent-runner/src/db/messages-out.ts +0 -143
- package/container/agent-runner/src/db/session-routing.ts +0 -30
- package/container/agent-runner/src/db/session-state.test.ts +0 -100
- package/container/agent-runner/src/db/session-state.ts +0 -79
- package/container/agent-runner/src/destinations.ts +0 -135
- package/container/agent-runner/src/formatter.test.ts +0 -167
- package/container/agent-runner/src/formatter.ts +0 -260
- package/container/agent-runner/src/index.ts +0 -110
- package/container/agent-runner/src/integration.test.ts +0 -121
- package/container/agent-runner/src/mcp-tools/agents.instructions.md +0 -26
- package/container/agent-runner/src/mcp-tools/agents.ts +0 -66
- package/container/agent-runner/src/mcp-tools/core.instructions.md +0 -27
- package/container/agent-runner/src/mcp-tools/core.ts +0 -262
- package/container/agent-runner/src/mcp-tools/index.ts +0 -22
- package/container/agent-runner/src/mcp-tools/interactive.instructions.md +0 -22
- package/container/agent-runner/src/mcp-tools/interactive.ts +0 -169
- package/container/agent-runner/src/mcp-tools/scheduling.instructions.md +0 -40
- package/container/agent-runner/src/mcp-tools/scheduling.ts +0 -299
- package/container/agent-runner/src/mcp-tools/self-mod.instructions.md +0 -25
- package/container/agent-runner/src/mcp-tools/self-mod.ts +0 -120
- package/container/agent-runner/src/mcp-tools/server.ts +0 -54
- package/container/agent-runner/src/mcp-tools/types.ts +0 -6
- package/container/agent-runner/src/poll-loop.test.ts +0 -248
- package/container/agent-runner/src/poll-loop.ts +0 -437
- package/container/agent-runner/src/providers/claude.ts +0 -379
- package/container/agent-runner/src/providers/factory.test.ts +0 -19
- package/container/agent-runner/src/providers/factory.ts +0 -13
- package/container/agent-runner/src/providers/index.ts +0 -6
- package/container/agent-runner/src/providers/mock.ts +0 -77
- package/container/agent-runner/src/providers/provider-registry.ts +0 -33
- package/container/agent-runner/src/providers/types.ts +0 -82
- package/container/agent-runner/src/scheduling/task-script.ts +0 -121
- package/container/agent-runner/src/timezone.test.ts +0 -93
- package/container/agent-runner/src/timezone.ts +0 -107
- package/container/agent-runner/tsconfig.json +0 -14
- package/container/build.sh +0 -48
- package/container/entrypoint.sh +0 -16
- package/container/skills/agent-browser/SKILL.md +0 -159
- package/container/skills/frontend-engineer/SKILL.md +0 -157
- package/container/skills/self-customize/SKILL.md +0 -87
- package/container/skills/slack-formatting/SKILL.md +0 -94
- package/container/skills/vercel-cli/SKILL.md +0 -111
- package/container/skills/welcome/SKILL.md +0 -85
- package/docs/APPLE-CONTAINER-NETWORKING.md +0 -90
- package/docs/BRANCH-FORK-MAINTENANCE.md +0 -81
- package/docs/README.md +0 -25
- package/docs/SDK_DEEP_DIVE.md +0 -643
- package/docs/SECURITY.md +0 -162
- package/docs/agent-runner-details.md +0 -749
- package/docs/api-details.md +0 -365
- package/docs/architecture-diagram.html +0 -422
- package/docs/architecture-diagram.md +0 -215
- package/docs/architecture.md +0 -751
- package/docs/audit/2026-04-30-channel-endpoint-audit.md +0 -36
- package/docs/build-and-runtime.md +0 -80
- package/docs/cross-mount-stress/README.md +0 -112
- package/docs/cross-mount-stress/container-writer-retry.mjs +0 -55
- package/docs/cross-mount-stress/container-writer-slow.mjs +0 -42
- package/docs/cross-mount-stress/container-writer.mjs +0 -47
- package/docs/cross-mount-stress/host-writer-retry.mjs +0 -55
- package/docs/cross-mount-stress/host-writer-slow.mjs +0 -43
- package/docs/cross-mount-stress/host-writer.mjs +0 -47
- package/docs/db-central.md +0 -316
- package/docs/db-session.md +0 -183
- package/docs/db.md +0 -119
- package/docs/design/2026-04-29-vault-management-ui.md +0 -231
- package/docs/design/2026-04-30-channel-wiring-rework.md +0 -234
- package/docs/design/2026-05-01-channel-wiring-approvals-deep-dive.md +0 -272
- package/docs/design/2026-05-02-channel-policy-and-approval-routing.md +0 -250
- package/docs/docker-sandboxes.md +0 -359
- package/docs/isolation-model.md +0 -88
- package/docs/ollama.md +0 -79
- package/docs/parachute-integration.md +0 -109
- package/docs/post-night-rebirth-reflections.md +0 -151
- package/eslint.config.js +0 -32
- package/pnpm-workspace.yaml +0 -8
- package/repo-tokens/README.md +0 -113
- package/repo-tokens/action.yml +0 -186
- package/repo-tokens/badge.svg +0 -23
- package/repo-tokens/examples/green.svg +0 -14
- package/repo-tokens/examples/red.svg +0 -14
- package/repo-tokens/examples/yellow-green.svg +0 -14
- package/repo-tokens/examples/yellow.svg +0 -14
- package/scripts/chat.ts +0 -101
- package/scripts/cleanup-sessions.sh +0 -150
- package/scripts/init-cli-agent.ts +0 -172
- package/scripts/init-first-agent.ts +0 -378
- package/scripts/parachute.ts +0 -158
- package/scripts/run-migrations.ts +0 -105
- package/scripts/sanity-live-poll.ts +0 -95
- package/scripts/seed-discord.ts +0 -80
- package/scripts/test-v2-agent.ts +0 -106
- package/scripts/test-v2-channel-e2e.ts +0 -265
- package/scripts/test-v2-host.ts +0 -184
- package/src/channels/adapter.ts +0 -214
- package/src/channels/api-translator.test.ts +0 -306
- package/src/channels/api-translator.ts +0 -214
- package/src/channels/ask-question.ts +0 -46
- package/src/channels/channel-registry.test.ts +0 -421
- package/src/channels/channel-registry.ts +0 -313
- package/src/channels/chat-sdk-bridge.test.ts +0 -84
- package/src/channels/chat-sdk-bridge.ts +0 -652
- package/src/channels/cli.ts +0 -276
- package/src/channels/discord.ts +0 -90
- package/src/channels/index.ts +0 -17
- package/src/channels/telegram-markdown-sanitize.test.ts +0 -78
- package/src/channels/telegram-markdown-sanitize.ts +0 -55
- package/src/channels/telegram-pairing.test.ts +0 -254
- package/src/channels/telegram-pairing.ts +0 -339
- package/src/channels/telegram.ts +0 -279
- package/src/channels/trust-hint.test.ts +0 -48
- package/src/channels/trust-hint.ts +0 -75
- package/src/claude-md-compose.migrate.test.ts +0 -64
- package/src/claude-md-compose.ts +0 -205
- package/src/command-gate.ts +0 -63
- package/src/config.test.ts +0 -93
- package/src/config.ts +0 -128
- package/src/container-config.ts +0 -167
- package/src/container-runner.test.ts +0 -32
- package/src/container-runner.ts +0 -576
- package/src/container-runtime.test.ts +0 -269
- package/src/container-runtime.ts +0 -167
- package/src/db/_bun-sqlite-shim.ts +0 -88
- package/src/db/agent-activity.test.ts +0 -155
- package/src/db/agent-activity.ts +0 -121
- package/src/db/agent-groups.ts +0 -77
- package/src/db/connection.migrate.test.ts +0 -176
- package/src/db/connection.ts +0 -259
- package/src/db/db-v2.test.ts +0 -440
- package/src/db/dropped-messages.ts +0 -44
- package/src/db/index.ts +0 -40
- package/src/db/messaging-groups.ts +0 -252
- package/src/db/migrations/001-initial.ts +0 -112
- package/src/db/migrations/002-chat-sdk-state.ts +0 -36
- package/src/db/migrations/008-dropped-messages.ts +0 -27
- package/src/db/migrations/009-drop-pending-credentials.ts +0 -13
- package/src/db/migrations/010-engage-modes.ts +0 -103
- package/src/db/migrations/011-pending-sender-approvals.ts +0 -40
- package/src/db/migrations/012-channel-registration.ts +0 -48
- package/src/db/migrations/013-approval-render-metadata.ts +0 -27
- package/src/db/migrations/014-secrets.ts +0 -44
- package/src/db/migrations/015-secrets-drop-host-pattern.ts +0 -18
- package/src/db/migrations/016-secret-assignments.ts +0 -30
- package/src/db/migrations/017-agent-activity.ts +0 -40
- package/src/db/migrations/018-oauth-app-configs.ts +0 -34
- package/src/db/migrations/019-oauth-app-connections.ts +0 -48
- package/src/db/migrations/020-agent-app-connections.ts +0 -28
- package/src/db/migrations/021-pending-oauth-states.ts +0 -35
- package/src/db/migrations/022-app-connections-provider.ts +0 -25
- package/src/db/migrations/023-agent-group-secret-mode.test.ts +0 -124
- package/src/db/migrations/023-agent-group-secret-mode.ts +0 -65
- package/src/db/migrations/024-collapse-approvals.test.ts +0 -249
- package/src/db/migrations/024-collapse-approvals.ts +0 -182
- package/src/db/migrations/025-secret-mode-check.test.ts +0 -155
- package/src/db/migrations/025-secret-mode-check.ts +0 -49
- package/src/db/migrations/026-user-dms-bot-id.test.ts +0 -116
- package/src/db/migrations/026-user-dms-bot-id.ts +0 -54
- package/src/db/migrations/027-provider-credentials.ts +0 -41
- package/src/db/migrations/_test-helpers.ts +0 -41
- package/src/db/migrations/index.ts +0 -127
- package/src/db/migrations/module-agent-to-agent-destinations.ts +0 -84
- package/src/db/migrations/module-approvals-pending-approvals.ts +0 -42
- package/src/db/migrations/module-approvals-title-options.ts +0 -40
- package/src/db/schema.ts +0 -258
- package/src/db/session-db.test.ts +0 -93
- package/src/db/session-db.ts +0 -325
- package/src/db/sessions.ts +0 -241
- package/src/delivery.test.ts +0 -148
- package/src/delivery.ts +0 -445
- package/src/env.ts +0 -74
- package/src/group-folder.test.ts +0 -35
- package/src/group-folder.ts +0 -44
- package/src/group-init.ts +0 -92
- package/src/host-core.test.ts +0 -456
- package/src/host-sweep.test.ts +0 -146
- package/src/host-sweep.ts +0 -287
- package/src/index.ts +0 -232
- package/src/install-slug.ts +0 -33
- package/src/log.test.ts +0 -81
- package/src/log.ts +0 -117
- package/src/mcp/http.ts +0 -72
- package/src/mcp/server.ts +0 -92
- package/src/mcp/stdio.ts +0 -51
- package/src/mcp/tools/activity.ts +0 -88
- package/src/mcp/tools/agent-groups.ts +0 -183
- package/src/mcp/tools/approvals.ts +0 -122
- package/src/mcp/tools/channels.test.ts +0 -126
- package/src/mcp/tools/channels.ts +0 -134
- package/src/mcp/tools/index.ts +0 -27
- package/src/mcp/tools/oauth.ts +0 -48
- package/src/mcp/tools/secrets.ts +0 -169
- package/src/mcp/tools/sessions.ts +0 -135
- package/src/mcp/types.ts +0 -51
- package/src/modules/agent-to-agent/agent-route.test.ts +0 -46
- package/src/modules/agent-to-agent/agent-route.ts +0 -223
- package/src/modules/agent-to-agent/create-agent.ts +0 -127
- package/src/modules/agent-to-agent/db/agent-destinations.ts +0 -135
- package/src/modules/agent-to-agent/index.ts +0 -22
- package/src/modules/agent-to-agent/write-destinations.ts +0 -59
- package/src/modules/approvals/agent.md +0 -45
- package/src/modules/approvals/index.ts +0 -21
- package/src/modules/approvals/picks.test.ts +0 -291
- package/src/modules/approvals/primitive.ts +0 -279
- package/src/modules/approvals/project.md +0 -27
- package/src/modules/approvals/response-handler.ts +0 -87
- package/src/modules/index.ts +0 -24
- package/src/modules/interactive/agent.md +0 -21
- package/src/modules/interactive/index.ts +0 -69
- package/src/modules/interactive/project.md +0 -12
- package/src/modules/mount-security/expand-path.test.ts +0 -82
- package/src/modules/mount-security/index.ts +0 -459
- package/src/modules/mount-security/migrate.test.ts +0 -91
- package/src/modules/permissions/access.ts +0 -28
- package/src/modules/permissions/channel-approval.test.ts +0 -389
- package/src/modules/permissions/channel-approval.ts +0 -188
- package/src/modules/permissions/db/agent-group-members.ts +0 -44
- package/src/modules/permissions/db/pending-channel-approvals.test.ts +0 -86
- package/src/modules/permissions/db/pending-channel-approvals.ts +0 -66
- package/src/modules/permissions/db/pending-sender-approvals.ts +0 -60
- package/src/modules/permissions/db/user-dms.ts +0 -58
- package/src/modules/permissions/db/user-roles.ts +0 -85
- package/src/modules/permissions/db/users.ts +0 -38
- package/src/modules/permissions/index.ts +0 -421
- package/src/modules/permissions/permissions.test.ts +0 -358
- package/src/modules/permissions/sender-approval.test.ts +0 -641
- package/src/modules/permissions/sender-approval.ts +0 -165
- package/src/modules/permissions/user-dm.ts +0 -200
- package/src/modules/provider-credentials/db.ts +0 -121
- package/src/modules/provider-credentials/index.ts +0 -12
- package/src/modules/provider-credentials/spawn.test.ts +0 -206
- package/src/modules/provider-credentials/spawn.ts +0 -114
- package/src/modules/scheduling/actions.ts +0 -113
- package/src/modules/scheduling/db.test.ts +0 -282
- package/src/modules/scheduling/db.ts +0 -148
- package/src/modules/scheduling/index.ts +0 -34
- package/src/modules/scheduling/recurrence.test.ts +0 -98
- package/src/modules/scheduling/recurrence.ts +0 -54
- package/src/modules/self-mod/agent.md +0 -30
- package/src/modules/self-mod/apply.ts +0 -85
- package/src/modules/self-mod/index.ts +0 -30
- package/src/modules/self-mod/project.md +0 -39
- package/src/modules/self-mod/request.ts +0 -91
- package/src/modules/typing/index.ts +0 -165
- package/src/oauth/agent-app-connections.ts +0 -103
- package/src/oauth/app-configs.test.ts +0 -64
- package/src/oauth/app-configs.ts +0 -114
- package/src/oauth/app-connections.test.ts +0 -109
- package/src/oauth/app-connections.ts +0 -178
- package/src/oauth/crypto.ts +0 -56
- package/src/oauth/flow.ts +0 -104
- package/src/oauth/providers/google.test.ts +0 -38
- package/src/oauth/providers/google.ts +0 -46
- package/src/oauth/providers/index.ts +0 -48
- package/src/oauth/state-store.test.ts +0 -54
- package/src/oauth/state-store.ts +0 -93
- package/src/parachute/README.md +0 -27
- package/src/parachute/create-agent.test.ts +0 -83
- package/src/parachute/create-agent.ts +0 -122
- package/src/parachute/group-status.test.ts +0 -165
- package/src/parachute/group-status.ts +0 -136
- package/src/parachute/types.ts +0 -41
- package/src/parachute/vault-mcp.test.ts +0 -251
- package/src/parachute/vault-mcp.ts +0 -232
- package/src/platform-id.test.ts +0 -104
- package/src/platform-id.ts +0 -109
- package/src/providers/index.ts +0 -6
- package/src/providers/provider-container-registry.ts +0 -58
- package/src/response-registry.ts +0 -45
- package/src/router.ts +0 -530
- package/src/secrets/crypto.test.ts +0 -45
- package/src/secrets/crypto.ts +0 -55
- package/src/secrets/index.ts +0 -461
- package/src/secrets/master-key.ts +0 -70
- package/src/secrets/secrets.test.ts +0 -651
- package/src/session-manager.attachments.test.ts +0 -171
- package/src/session-manager.dup-skip.test.ts +0 -173
- package/src/session-manager.migrate.test.ts +0 -59
- package/src/session-manager.ts +0 -451
- package/src/startup-bootstrap.test.ts +0 -226
- package/src/startup-bootstrap.ts +0 -207
- package/src/state-sqlite.ts +0 -182
- package/src/timezone.test.ts +0 -64
- package/src/timezone.ts +0 -37
- package/src/types.ts +0 -233
- package/src/web/auth.test.ts +0 -335
- package/src/web/auth.ts +0 -214
- package/src/web/discord-validate.test.ts +0 -77
- package/src/web/discord-validate.ts +0 -88
- package/src/web/hub-discovery.test.ts +0 -98
- package/src/web/hub-discovery.ts +0 -69
- package/src/web/routes/activity.ts +0 -106
- package/src/web/routes/agent-provider.test.ts +0 -282
- package/src/web/routes/agent-provider.ts +0 -309
- package/src/web/routes/approvals.ts +0 -185
- package/src/web/routes/apps.ts +0 -434
- package/src/web/routes/channels-mg-detail.test.ts +0 -324
- package/src/web/routes/channels-mga-detail.test.ts +0 -472
- package/src/web/routes/channels.ts +0 -311
- package/src/web/routes/oauth-providers.ts +0 -42
- package/src/web/routes/secrets.test.ts +0 -220
- package/src/web/routes/secrets.ts +0 -317
- package/src/web/routes/sessions.ts +0 -123
- package/src/web/routes/settings.test.ts +0 -106
- package/src/web/routes/settings.ts +0 -247
- package/src/web/routes/setup-status.ts +0 -205
- package/src/web/routes/vaults.test.ts +0 -389
- package/src/web/routes/vaults.ts +0 -225
- package/src/web/server-version.test.ts +0 -16
- package/src/web/server.ts +0 -1024
- package/src/web/services-manifest.test.ts +0 -148
- package/src/web/services-manifest.ts +0 -66
- package/src/web/static-serve.test.ts +0 -255
- package/src/web/static-serve.ts +0 -104
- package/src/web/telegram-validate.test.ts +0 -116
- package/src/web/telegram-validate.ts +0 -107
- package/src/web/vault-proxy.test.ts +0 -214
- package/src/web/vault-proxy.ts +0 -120
- package/src/web/wire-channel.ts +0 -181
- package/src/webhook-server.ts +0 -134
- package/vitest.config.ts +0 -18
- package/web/README.md +0 -63
- package/web/ui/index.html +0 -13
- package/web/ui/package.json +0 -35
- package/web/ui/pnpm-lock.yaml +0 -2164
- package/web/ui/scripts/verify-base.mjs +0 -31
- package/web/ui/src/App.tsx +0 -88
- package/web/ui/src/components/ActivityFeed.tsx +0 -444
- package/web/ui/src/components/AgentGroupPicker.tsx +0 -263
- package/web/ui/src/components/AgentProviderCards.tsx +0 -220
- package/web/ui/src/components/CredentialForm.tsx +0 -214
- package/web/ui/src/components/ScopeGrants.tsx +0 -74
- package/web/ui/src/components/StatusDot.tsx +0 -43
- package/web/ui/src/components/VaultPicker.tsx +0 -127
- package/web/ui/src/components/setup/AdapterInstallStep.tsx +0 -178
- package/web/ui/src/components/setup/AgentGroupStep.tsx +0 -43
- package/web/ui/src/components/setup/ChannelPickStep.tsx +0 -74
- package/web/ui/src/components/setup/DoneStep.tsx +0 -49
- package/web/ui/src/components/setup/PrereqStep.tsx +0 -129
- package/web/ui/src/components/setup/TestConnectionStep.tsx +0 -108
- package/web/ui/src/components/setup/TestMessageStep.tsx +0 -104
- package/web/ui/src/components/setup/WireChannelStep.tsx +0 -166
- package/web/ui/src/components/setup/types.ts +0 -105
- package/web/ui/src/lib/api.test.ts +0 -410
- package/web/ui/src/lib/api.ts +0 -1248
- package/web/ui/src/lib/auth.test.ts +0 -352
- package/web/ui/src/lib/auth.ts +0 -405
- package/web/ui/src/lib/channel-adapters.ts +0 -136
- package/web/ui/src/main.tsx +0 -19
- package/web/ui/src/routes/ApprovalsList.tsx +0 -294
- package/web/ui/src/routes/Apps.tsx +0 -613
- package/web/ui/src/routes/ChannelWireDetail.test.tsx +0 -233
- package/web/ui/src/routes/ChannelWireDetail.tsx +0 -403
- package/web/ui/src/routes/ChannelsList.tsx +0 -158
- package/web/ui/src/routes/GroupDetail.test.tsx +0 -206
- package/web/ui/src/routes/GroupDetail.tsx +0 -880
- package/web/ui/src/routes/GroupList.tsx +0 -187
- package/web/ui/src/routes/MessagingGroupDetail.test.tsx +0 -233
- package/web/ui/src/routes/MessagingGroupDetail.tsx +0 -306
- package/web/ui/src/routes/NewGroupWizard.tsx +0 -390
- package/web/ui/src/routes/OAuthCallback.tsx +0 -56
- package/web/ui/src/routes/SecretsList.tsx +0 -942
- package/web/ui/src/routes/SessionsList.tsx +0 -220
- package/web/ui/src/routes/SettingsAgentProvider.tsx +0 -109
- package/web/ui/src/routes/SettingsApprovals.tsx +0 -234
- package/web/ui/src/routes/SetupWizard.tsx +0 -219
- package/web/ui/src/routes/VaultDetail.test.tsx +0 -363
- package/web/ui/src/routes/VaultDetail.tsx +0 -960
- package/web/ui/src/routes/VaultsList.tsx +0 -295
- package/web/ui/src/routes/WireChannelPage.tsx +0 -413
- package/web/ui/src/styles.css +0 -608
- package/web/ui/src/test/setup.ts +0 -23
- package/web/ui/src/vite-env.d.ts +0 -10
- package/web/ui/vite.config.ts +0 -34
- package/web/ui/vitest.config.ts +0 -25
package/src/router.ts
DELETED
|
@@ -1,530 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Inbound message routing.
|
|
3
|
-
*
|
|
4
|
-
* Channel adapter event → resolve messaging group → sender resolver →
|
|
5
|
-
* resolve/pick agent → access gate → resolve/create session → write
|
|
6
|
-
* messages_in → wake container.
|
|
7
|
-
*
|
|
8
|
-
* Two module hooks (registered by the permissions module):
|
|
9
|
-
* - `setSenderResolver` runs BEFORE agent resolution so user rows get
|
|
10
|
-
* upserted even if the message ends up dropped by agent wiring.
|
|
11
|
-
* Without the module, userId is null and downstream code tolerates it.
|
|
12
|
-
* - `setAccessGate` runs AFTER agent resolution so policy decisions can
|
|
13
|
-
* branch on the target agent group. Without the module, access is
|
|
14
|
-
* allow-all.
|
|
15
|
-
*
|
|
16
|
-
* `dropped_messages` is core audit infra. Core writes rows for structural
|
|
17
|
-
* drops (no agent wired, no trigger match); the access gate writes rows
|
|
18
|
-
* for policy refusals.
|
|
19
|
-
*/
|
|
20
|
-
import { getChannelAdapter } from './channels/channel-registry.js';
|
|
21
|
-
import { consumeTrustHint } from './channels/trust-hint.js';
|
|
22
|
-
import { gateCommand } from './command-gate.js';
|
|
23
|
-
import { getAgentGroup, getAllAgentGroups } from './db/agent-groups.js';
|
|
24
|
-
import { recordDroppedMessage } from './db/dropped-messages.js';
|
|
25
|
-
import {
|
|
26
|
-
createMessagingGroup,
|
|
27
|
-
getMessagingGroupAgents,
|
|
28
|
-
getMessagingGroupWithAgentCount,
|
|
29
|
-
updateMessagingGroup,
|
|
30
|
-
} from './db/messaging-groups.js';
|
|
31
|
-
import { decodePlatformIdAs } from './platform-id.js';
|
|
32
|
-
import { wireDmToAgent } from './web/wire-channel.js';
|
|
33
|
-
import { findSessionForAgent } from './db/sessions.js';
|
|
34
|
-
import { startTypingRefresh } from './modules/typing/index.js';
|
|
35
|
-
import { log } from './log.js';
|
|
36
|
-
import { resolveSession, writeSessionMessage, writeOutboundDirect } from './session-manager.js';
|
|
37
|
-
import { wakeContainer } from './container-runner.js';
|
|
38
|
-
import { getSession } from './db/sessions.js';
|
|
39
|
-
import type { AgentGroup, MessagingGroup, MessagingGroupAgent } from './types.js';
|
|
40
|
-
import type { InboundEvent } from './channels/adapter.js';
|
|
41
|
-
|
|
42
|
-
function generateId(): string {
|
|
43
|
-
return `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Sender-resolver hook. Runs before agent resolution.
|
|
48
|
-
*
|
|
49
|
-
* The permissions module registers this to extract the sender's namespaced
|
|
50
|
-
* user id and upsert the users row. Returns null when the payload doesn't
|
|
51
|
-
* carry enough info to identify a sender. Without the hook, every message
|
|
52
|
-
* arrives at the gate with userId=null.
|
|
53
|
-
*/
|
|
54
|
-
export type SenderResolverFn = (event: InboundEvent) => string | null;
|
|
55
|
-
|
|
56
|
-
let senderResolver: SenderResolverFn | null = null;
|
|
57
|
-
|
|
58
|
-
export function setSenderResolver(fn: SenderResolverFn): void {
|
|
59
|
-
if (senderResolver) {
|
|
60
|
-
log.warn('Sender resolver overwritten');
|
|
61
|
-
}
|
|
62
|
-
senderResolver = fn;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Access-gate hook. Runs after agent resolution.
|
|
67
|
-
*
|
|
68
|
-
* The permissions module registers this; without it, core defaults to
|
|
69
|
-
* allow-all. The gate receives the raw event so it can extract the sender
|
|
70
|
-
* name for audit-trail purposes, and it is responsible for recording its
|
|
71
|
-
* own `dropped_messages` row on refusal (structural drops are already
|
|
72
|
-
* recorded by core before the gate runs).
|
|
73
|
-
*/
|
|
74
|
-
export type AccessGateResult = { allowed: true } | { allowed: false; reason: string };
|
|
75
|
-
|
|
76
|
-
export type AccessGateFn = (
|
|
77
|
-
event: InboundEvent,
|
|
78
|
-
userId: string | null,
|
|
79
|
-
mg: MessagingGroup,
|
|
80
|
-
agentGroupId: string,
|
|
81
|
-
) => AccessGateResult;
|
|
82
|
-
|
|
83
|
-
let accessGate: AccessGateFn | null = null;
|
|
84
|
-
|
|
85
|
-
export function setAccessGate(fn: AccessGateFn): void {
|
|
86
|
-
if (accessGate) {
|
|
87
|
-
log.warn('Access gate overwritten');
|
|
88
|
-
}
|
|
89
|
-
accessGate = fn;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Per-wiring sender-scope hook. Runs alongside the access gate for each
|
|
94
|
-
* agent that would otherwise engage — lets the permissions module enforce
|
|
95
|
-
* `sender_scope='known'` on wirings that are stricter than the messaging
|
|
96
|
-
* group's `unknown_sender_policy`. When the hook isn't registered (module
|
|
97
|
-
* not installed), sender_scope is a no-op.
|
|
98
|
-
*/
|
|
99
|
-
export type SenderScopeGateFn = (
|
|
100
|
-
event: InboundEvent,
|
|
101
|
-
userId: string | null,
|
|
102
|
-
mg: MessagingGroup,
|
|
103
|
-
agent: MessagingGroupAgent,
|
|
104
|
-
) => AccessGateResult;
|
|
105
|
-
|
|
106
|
-
let senderScopeGate: SenderScopeGateFn | null = null;
|
|
107
|
-
|
|
108
|
-
export function setSenderScopeGate(fn: SenderScopeGateFn): void {
|
|
109
|
-
if (senderScopeGate) {
|
|
110
|
-
log.warn('Sender-scope gate overwritten');
|
|
111
|
-
}
|
|
112
|
-
senderScopeGate = fn;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Channel-registration hook. Runs when the router sees a mention/DM on a
|
|
117
|
-
* messaging group that has no wirings AND hasn't been denied. The hook is
|
|
118
|
-
* expected to escalate to an owner (card, etc.) and arrange for future
|
|
119
|
-
* replay via routeInbound after approval. Fire-and-forget from the
|
|
120
|
-
* router's perspective.
|
|
121
|
-
*
|
|
122
|
-
* Registered by the permissions module. Without the module the router
|
|
123
|
-
* silently records the drop with reason='no_agent_wired' and moves on.
|
|
124
|
-
*/
|
|
125
|
-
export type ChannelRequestGateFn = (mg: MessagingGroup, event: InboundEvent) => Promise<void>;
|
|
126
|
-
|
|
127
|
-
let channelRequestGate: ChannelRequestGateFn | null = null;
|
|
128
|
-
|
|
129
|
-
export function setChannelRequestGate(fn: ChannelRequestGateFn): void {
|
|
130
|
-
if (channelRequestGate) {
|
|
131
|
-
log.warn('Channel-request gate overwritten');
|
|
132
|
-
}
|
|
133
|
-
channelRequestGate = fn;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function safeParseContent(raw: string): { text?: string; sender?: string; senderId?: string } {
|
|
137
|
-
try {
|
|
138
|
-
return JSON.parse(raw);
|
|
139
|
-
} catch {
|
|
140
|
-
return { text: raw };
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Route an inbound message from a channel adapter to the correct session.
|
|
146
|
-
* Creates messaging group + session if they don't exist yet.
|
|
147
|
-
*/
|
|
148
|
-
export async function routeInbound(event: InboundEvent): Promise<void> {
|
|
149
|
-
// 0. Apply the adapter's thread policy. Non-threaded adapters (Telegram,
|
|
150
|
-
// WhatsApp, iMessage, email) collapse threads to the channel.
|
|
151
|
-
// By-channel-type (not by-bot) lookup is correct here: we only read
|
|
152
|
-
// `supportsThreads`, which is a property of the channel itself, not of
|
|
153
|
-
// a specific bot identity. Per-bot resolution (`getChannelAdapterForPlatformId`)
|
|
154
|
-
// is reserved for delivery, where the outbound adapter must match the
|
|
155
|
-
// bot dimension encoded in the v2 platform_id.
|
|
156
|
-
const adapter = getChannelAdapter(event.channelType);
|
|
157
|
-
if (adapter && !adapter.supportsThreads) {
|
|
158
|
-
event = { ...event, threadId: null };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const isMention = event.message.isMention === true;
|
|
162
|
-
|
|
163
|
-
// 1. Combined lookup: messaging_group row + count of wired agents in a
|
|
164
|
-
// single query. Cheap short-circuit for the common "unwired channel"
|
|
165
|
-
// case — one DB read and we're out, no auto-create, no sender
|
|
166
|
-
// resolution, no log spam.
|
|
167
|
-
const found = getMessagingGroupWithAgentCount(event.channelType, event.platformId);
|
|
168
|
-
|
|
169
|
-
let mg: MessagingGroup;
|
|
170
|
-
let agentCount: number;
|
|
171
|
-
if (!found) {
|
|
172
|
-
// No messaging_groups row. Auto-create only when the message warrants
|
|
173
|
-
// attention (the bot was addressed — @mention or DM). Plain chatter in
|
|
174
|
-
// channels we merely sit in stays silent — no row, no DB writes.
|
|
175
|
-
if (!isMention) return;
|
|
176
|
-
const mgId = `mg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
177
|
-
mg = {
|
|
178
|
-
id: mgId,
|
|
179
|
-
channel_type: event.channelType,
|
|
180
|
-
platform_id: event.platformId,
|
|
181
|
-
name: null,
|
|
182
|
-
is_group: event.message.isGroup ? 1 : 0,
|
|
183
|
-
unknown_sender_policy: 'request_approval',
|
|
184
|
-
denied_at: null,
|
|
185
|
-
created_at: new Date().toISOString(),
|
|
186
|
-
};
|
|
187
|
-
createMessagingGroup(mg);
|
|
188
|
-
log.info('Auto-created messaging group', {
|
|
189
|
-
id: mgId,
|
|
190
|
-
channelType: event.channelType,
|
|
191
|
-
platformId: event.platformId,
|
|
192
|
-
});
|
|
193
|
-
agentCount = 0;
|
|
194
|
-
} else {
|
|
195
|
-
mg = found.mg;
|
|
196
|
-
agentCount = found.agentCount;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// 1b. No wirings — either silent drop (plain chatter / denied channel) or
|
|
200
|
-
// escalate to owner for channel-registration approval.
|
|
201
|
-
if (agentCount === 0) {
|
|
202
|
-
if (!isMention) return;
|
|
203
|
-
if (mg.denied_at) {
|
|
204
|
-
log.debug('Message dropped — channel was denied by owner', {
|
|
205
|
-
messagingGroupId: mg.id,
|
|
206
|
-
deniedAt: mg.denied_at,
|
|
207
|
-
});
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const parsedUnwired = safeParseContent(event.message.content);
|
|
212
|
-
|
|
213
|
-
// Trust hint: if the operator just wired this bot at /channels/new and
|
|
214
|
-
// is now DM'ing it, treat the message as trusted self-traffic instead
|
|
215
|
-
// of escalating through the channel-registration approval flow. The
|
|
216
|
-
// hint is single-use and bound to (channelType, botId, operatorUserId)
|
|
217
|
-
// — Discord wires record no hint (no operator user id captured), so
|
|
218
|
-
// this branch only fires for Telegram self-DMs in the trust window.
|
|
219
|
-
const decoded = decodePlatformIdAs(event.platformId, 'v2');
|
|
220
|
-
const senderId = parsedUnwired.senderId;
|
|
221
|
-
if (decoded.botId && senderId && consumeTrustHint(event.channelType, decoded.botId, senderId)) {
|
|
222
|
-
const targetGroups = getAllAgentGroups();
|
|
223
|
-
if (targetGroups.length === 0) {
|
|
224
|
-
// Hint already consumed (single-use) but there's no agent group to
|
|
225
|
-
// wire to. Falls through to the approval cascade below; surface a
|
|
226
|
-
// warn so an operator who hits this can correlate it with their
|
|
227
|
-
// missing agent-group setup.
|
|
228
|
-
log.warn('Trust hint consumed but no agent groups — operator message dropped', {
|
|
229
|
-
messagingGroupId: mg.id,
|
|
230
|
-
channelType: event.channelType,
|
|
231
|
-
botId: decoded.botId,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
if (targetGroups.length > 0) {
|
|
235
|
-
// Multi-agent-group installs: this picks the first group by DB
|
|
236
|
-
// insert order. Good enough for the trust-hint use case (operator
|
|
237
|
-
// just wired a bot and is DM'ing it now — usually the install only
|
|
238
|
-
// has one group anyway), but a richer "wire to the most recently
|
|
239
|
-
// wired group" or operator-prompted pick is a follow-up if needed.
|
|
240
|
-
const target = targetGroups[0]!;
|
|
241
|
-
log.info('Channel inbound auto-wired via operator trust hint', {
|
|
242
|
-
messagingGroupId: mg.id,
|
|
243
|
-
channelType: event.channelType,
|
|
244
|
-
botId: decoded.botId,
|
|
245
|
-
targetAgentGroupId: target.id,
|
|
246
|
-
});
|
|
247
|
-
// Drop the auto-created request_approval row in favor of a fresh
|
|
248
|
-
// wire built with the trusted defaults (strict policy, all-senders
|
|
249
|
-
// MGA). wireDmToAgent is idempotent — if we beat it to a wire that
|
|
250
|
-
// already exists, it returns the existing rows.
|
|
251
|
-
updateMessagingGroup(mg.id, { unknown_sender_policy: 'strict' });
|
|
252
|
-
wireDmToAgent({
|
|
253
|
-
channelType: event.channelType as 'discord' | 'telegram',
|
|
254
|
-
agentGroup: target,
|
|
255
|
-
botId: decoded.botId,
|
|
256
|
-
botUserId: decoded.native,
|
|
257
|
-
});
|
|
258
|
-
// Re-run the standard route from the now-wired path so engage
|
|
259
|
-
// checks, sender resolution, and session creation behave exactly
|
|
260
|
-
// as if this were a normal message on a pre-wired channel.
|
|
261
|
-
await routeInbound(event);
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
recordDroppedMessage({
|
|
267
|
-
channel_type: event.channelType,
|
|
268
|
-
platform_id: event.platformId,
|
|
269
|
-
user_id: null,
|
|
270
|
-
sender_name: parsedUnwired.sender ?? null,
|
|
271
|
-
reason: 'no_agent_wired',
|
|
272
|
-
messaging_group_id: mg.id,
|
|
273
|
-
agent_group_id: null,
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
if (channelRequestGate) {
|
|
277
|
-
// Fire-and-forget escalation. The gate is expected to build a card,
|
|
278
|
-
// persist pending_channel_approvals, and replay the event via
|
|
279
|
-
// routeInbound after approval. Errors are logged internally — the
|
|
280
|
-
// user's message still stays dropped here either way.
|
|
281
|
-
void channelRequestGate(mg, event).catch((err) =>
|
|
282
|
-
log.error('Channel-request gate threw', { messagingGroupId: mg.id, err }),
|
|
283
|
-
);
|
|
284
|
-
} else {
|
|
285
|
-
log.warn('MESSAGE DROPPED — no agent groups wired and no channel-request gate registered', {
|
|
286
|
-
messagingGroupId: mg.id,
|
|
287
|
-
channelType: event.channelType,
|
|
288
|
-
platformId: event.platformId,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// 2. Sender resolution (permissions module upserts the users row as a
|
|
295
|
-
// side effect so later role/access lookups find a real record).
|
|
296
|
-
// Without the module, userId is null — downstream tolerates it.
|
|
297
|
-
const userId: string | null = senderResolver ? senderResolver(event) : null;
|
|
298
|
-
|
|
299
|
-
// 3. Fetch wired agents in full (we already know the count is > 0; now
|
|
300
|
-
// we need their actual rows for fan-out).
|
|
301
|
-
const agents = getMessagingGroupAgents(mg.id);
|
|
302
|
-
|
|
303
|
-
// 4. Fan-out: evaluate each wired agent independently against engage_mode,
|
|
304
|
-
// sender_scope, and access gate. An agent that engages gets its own
|
|
305
|
-
// session and container wake. An agent that declines but has
|
|
306
|
-
// ignored_message_policy='accumulate' still gets the message stored in
|
|
307
|
-
// its session (trigger=0) so the context is available when it does
|
|
308
|
-
// engage later. Drop policy = skip silently.
|
|
309
|
-
//
|
|
310
|
-
// Subscribe (for mention-sticky wirings on threaded platforms) fires
|
|
311
|
-
// once per message from this loop — the first engaging mention-sticky
|
|
312
|
-
// wiring triggers adapter.subscribe(...); subsequent wirings don't
|
|
313
|
-
// re-subscribe (chat.subscribe is idempotent anyway, but the flag
|
|
314
|
-
// avoids the extra await).
|
|
315
|
-
const parsed = safeParseContent(event.message.content);
|
|
316
|
-
const messageText = parsed.text ?? '';
|
|
317
|
-
|
|
318
|
-
let engagedCount = 0;
|
|
319
|
-
let accumulatedCount = 0;
|
|
320
|
-
let subscribed = false;
|
|
321
|
-
|
|
322
|
-
for (const agent of agents) {
|
|
323
|
-
const agentGroup = getAgentGroup(agent.agent_group_id);
|
|
324
|
-
if (!agentGroup) continue;
|
|
325
|
-
|
|
326
|
-
const engages = evaluateEngage(agent, messageText, isMention, mg, event.threadId);
|
|
327
|
-
|
|
328
|
-
const accessOk = engages && (!accessGate || accessGate(event, userId, mg, agent.agent_group_id).allowed);
|
|
329
|
-
const scopeOk = engages && (!senderScopeGate || senderScopeGate(event, userId, mg, agent).allowed);
|
|
330
|
-
|
|
331
|
-
if (engages && accessOk && scopeOk) {
|
|
332
|
-
await deliverToAgent(agent, agentGroup, mg, event, userId, adapter?.supportsThreads === true, true);
|
|
333
|
-
engagedCount++;
|
|
334
|
-
|
|
335
|
-
// Mention-sticky: ask the adapter to subscribe the thread so the
|
|
336
|
-
// platform's subscribed-message path carries follow-ups without
|
|
337
|
-
// requiring another @mention. Threaded-adapter only; DMs and
|
|
338
|
-
// non-threaded platforms skip.
|
|
339
|
-
if (
|
|
340
|
-
!subscribed &&
|
|
341
|
-
agent.engage_mode === 'mention-sticky' &&
|
|
342
|
-
adapter?.supportsThreads &&
|
|
343
|
-
adapter.subscribe &&
|
|
344
|
-
event.threadId !== null &&
|
|
345
|
-
mg.is_group !== 0
|
|
346
|
-
) {
|
|
347
|
-
subscribed = true;
|
|
348
|
-
// Fire-and-forget — subscribe is platform-side bookkeeping and
|
|
349
|
-
// shouldn't block message routing. Errors are logged inside the
|
|
350
|
-
// adapter (or by the promise rejection handler below).
|
|
351
|
-
void adapter.subscribe(event.platformId, event.threadId).catch((err) => {
|
|
352
|
-
log.warn('adapter.subscribe failed', { channelType: event.channelType, threadId: event.threadId, err });
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
} else if (agent.ignored_message_policy === 'accumulate') {
|
|
356
|
-
await deliverToAgent(agent, agentGroup, mg, event, userId, adapter?.supportsThreads === true, false);
|
|
357
|
-
accumulatedCount++;
|
|
358
|
-
} else {
|
|
359
|
-
log.debug('Message not engaged for agent (drop policy)', {
|
|
360
|
-
agentGroupId: agent.agent_group_id,
|
|
361
|
-
engage_mode: agent.engage_mode,
|
|
362
|
-
engages,
|
|
363
|
-
accessOk,
|
|
364
|
-
scopeOk,
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (engagedCount + accumulatedCount === 0) {
|
|
370
|
-
recordDroppedMessage({
|
|
371
|
-
channel_type: event.channelType,
|
|
372
|
-
platform_id: event.platformId,
|
|
373
|
-
user_id: userId,
|
|
374
|
-
sender_name: parsed.sender ?? null,
|
|
375
|
-
reason: 'no_agent_engaged',
|
|
376
|
-
messaging_group_id: mg.id,
|
|
377
|
-
agent_group_id: null,
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Decide whether a given wired agent should engage on this message.
|
|
384
|
-
*
|
|
385
|
-
* 'pattern' — regex test on text; '.' = always
|
|
386
|
-
* 'mention' — bot must be mentioned on the platform. Resolved by
|
|
387
|
-
* the adapter (SDK-level) and forwarded as
|
|
388
|
-
* `event.message.isMention`. Agent display name
|
|
389
|
-
* (`agent_group.name`) is irrelevant — users address
|
|
390
|
-
* the bot via its platform username (@botname on
|
|
391
|
-
* Telegram, user-id mention on Slack/Discord), not
|
|
392
|
-
* via the agent's Paraclaw-side display name. If a
|
|
393
|
-
* user wants to disambiguate between multiple agents
|
|
394
|
-
* wired to one chat, use engage_mode='pattern' with
|
|
395
|
-
* the disambiguator as the regex.
|
|
396
|
-
* 'mention-sticky' — platform mention OR an active per-thread session
|
|
397
|
-
* already exists for this (agent, mg, thread). The
|
|
398
|
-
* session existence IS our subscription state; once
|
|
399
|
-
* a thread has engaged us once, follow-ups arrive
|
|
400
|
-
* with no mention and should still fire.
|
|
401
|
-
*/
|
|
402
|
-
function evaluateEngage(
|
|
403
|
-
agent: MessagingGroupAgent,
|
|
404
|
-
text: string,
|
|
405
|
-
isMention: boolean,
|
|
406
|
-
mg: MessagingGroup,
|
|
407
|
-
threadId: string | null,
|
|
408
|
-
): boolean {
|
|
409
|
-
switch (agent.engage_mode) {
|
|
410
|
-
case 'pattern': {
|
|
411
|
-
const pat = agent.engage_pattern ?? '.';
|
|
412
|
-
if (pat === '.') return true;
|
|
413
|
-
try {
|
|
414
|
-
return new RegExp(pat).test(text);
|
|
415
|
-
} catch {
|
|
416
|
-
// Bad regex: fail open so admin sees the agent responding + can fix.
|
|
417
|
-
return true;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
case 'mention':
|
|
421
|
-
return isMention;
|
|
422
|
-
case 'mention-sticky': {
|
|
423
|
-
if (isMention) return true;
|
|
424
|
-
// Sticky follow-up: session already exists for this (agent, mg, thread)
|
|
425
|
-
// — the thread was activated before, keep firing.
|
|
426
|
-
if (mg.is_group === 0) return false; // DMs never use mention-sticky sensibly
|
|
427
|
-
const existing = findSessionForAgent(agent.agent_group_id, mg.id, threadId);
|
|
428
|
-
return existing !== undefined;
|
|
429
|
-
}
|
|
430
|
-
default:
|
|
431
|
-
return false;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
async function deliverToAgent(
|
|
436
|
-
agent: MessagingGroupAgent,
|
|
437
|
-
agentGroup: AgentGroup,
|
|
438
|
-
mg: MessagingGroup,
|
|
439
|
-
event: InboundEvent,
|
|
440
|
-
userId: string | null,
|
|
441
|
-
adapterSupportsThreads: boolean,
|
|
442
|
-
wake: boolean,
|
|
443
|
-
): Promise<void> {
|
|
444
|
-
// Apply the adapter thread policy: threaded adapter in a group chat →
|
|
445
|
-
// per-thread session regardless of wiring. agent-shared preserved (it's
|
|
446
|
-
// a cross-channel directive the adapter doesn't know about). DMs collapse
|
|
447
|
-
// sub-threads to one session (is_group=0 short-circuit).
|
|
448
|
-
let effectiveSessionMode = agent.session_mode;
|
|
449
|
-
if (adapterSupportsThreads && effectiveSessionMode !== 'agent-shared' && mg.is_group !== 0) {
|
|
450
|
-
effectiveSessionMode = 'per-thread';
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const { session, created } = resolveSession(agent.agent_group_id, mg.id, event.threadId, effectiveSessionMode);
|
|
454
|
-
|
|
455
|
-
// The inbound row's (channel_type, platform_id, thread_id) is the address
|
|
456
|
-
// the agent's reply will be delivered to. Normally it mirrors the source
|
|
457
|
-
// (stamped from the event). When the caller supplied `replyTo` (CLI admin
|
|
458
|
-
// transport acting on operator intent), the reply is redirected there.
|
|
459
|
-
const deliveryAddr = event.replyTo ?? {
|
|
460
|
-
channelType: event.channelType,
|
|
461
|
-
platformId: event.platformId,
|
|
462
|
-
threadId: event.threadId,
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
// Command gate: classify slash commands before they reach the container.
|
|
466
|
-
// Filtered commands are dropped silently. Denied admin commands get a
|
|
467
|
-
// permission-denied response written directly to messages_out.
|
|
468
|
-
if (event.message.kind === 'chat' || event.message.kind === 'chat-sdk') {
|
|
469
|
-
const gate = gateCommand(event.message.content, userId, agent.agent_group_id);
|
|
470
|
-
if (gate.action === 'filter') {
|
|
471
|
-
log.debug('Filtered command dropped by gate', { agentGroupId: agent.agent_group_id });
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
if (gate.action === 'deny') {
|
|
475
|
-
writeOutboundDirect(session.agent_group_id, session.id, {
|
|
476
|
-
id: `deny-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
477
|
-
kind: 'chat',
|
|
478
|
-
platformId: deliveryAddr.platformId,
|
|
479
|
-
channelType: deliveryAddr.channelType,
|
|
480
|
-
threadId: deliveryAddr.threadId,
|
|
481
|
-
content: JSON.stringify({ text: `Permission denied: ${gate.command} requires admin access.` }),
|
|
482
|
-
});
|
|
483
|
-
log.info('Admin command denied by gate', { command: gate.command, userId, agentGroupId: agent.agent_group_id });
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
writeSessionMessage(session.agent_group_id, session.id, {
|
|
489
|
-
id: messageIdForAgent(event.message.id, agent.agent_group_id),
|
|
490
|
-
kind: event.message.kind,
|
|
491
|
-
timestamp: event.message.timestamp,
|
|
492
|
-
platformId: deliveryAddr.platformId,
|
|
493
|
-
channelType: deliveryAddr.channelType,
|
|
494
|
-
threadId: deliveryAddr.threadId,
|
|
495
|
-
content: event.message.content,
|
|
496
|
-
trigger: wake ? 1 : 0,
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
log.info('Message routed', {
|
|
500
|
-
sessionId: session.id,
|
|
501
|
-
agentGroup: agent.agent_group_id,
|
|
502
|
-
engage_mode: agent.engage_mode,
|
|
503
|
-
kind: event.message.kind,
|
|
504
|
-
userId,
|
|
505
|
-
wake,
|
|
506
|
-
created,
|
|
507
|
-
agentGroupName: agentGroup.name,
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
if (wake) {
|
|
511
|
-
// Typing indicator + wake are only for the engaged branch; accumulated
|
|
512
|
-
// messages sit silently until a real trigger fires.
|
|
513
|
-
startTypingRefresh(session.id, session.agent_group_id, event.channelType, event.platformId, event.threadId);
|
|
514
|
-
const freshSession = getSession(session.id);
|
|
515
|
-
if (freshSession) {
|
|
516
|
-
await wakeContainer(freshSession);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* When fanning out, the same inbound message lands in multiple per-agent
|
|
523
|
-
* session DBs. messages_in.id is PRIMARY KEY, so reuse of the raw id would
|
|
524
|
-
* collide across sessions (or, more subtly, within one session if re-routed
|
|
525
|
-
* after a retry). Namespace by agent_group_id to keep ids unique per session.
|
|
526
|
-
*/
|
|
527
|
-
function messageIdForAgent(baseId: string | undefined, agentGroupId: string): string {
|
|
528
|
-
const id = baseId && baseId.length > 0 ? baseId : generateId();
|
|
529
|
-
return `${id}:${agentGroupId}`;
|
|
530
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
|
|
4
|
-
import { decryptSecret, encryptSecret } from './crypto.js';
|
|
5
|
-
|
|
6
|
-
describe('secret crypto', () => {
|
|
7
|
-
const key = crypto.randomBytes(32);
|
|
8
|
-
|
|
9
|
-
it('round-trips a plaintext value', () => {
|
|
10
|
-
const ct = encryptSecret('xoxb-1234-secret', key);
|
|
11
|
-
expect(decryptSecret(ct, key)).toBe('xoxb-1234-secret');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('produces a different ciphertext each call (random IV)', () => {
|
|
15
|
-
const a = encryptSecret('same-value', key);
|
|
16
|
-
const b = encryptSecret('same-value', key);
|
|
17
|
-
expect(a).not.toBe(b);
|
|
18
|
-
expect(decryptSecret(a, key)).toBe('same-value');
|
|
19
|
-
expect(decryptSecret(b, key)).toBe('same-value');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('rejects tampered ciphertext', () => {
|
|
23
|
-
const ct = encryptSecret('original', key);
|
|
24
|
-
const buf = Buffer.from(ct, 'base64');
|
|
25
|
-
buf[buf.length - 1] ^= 0x01;
|
|
26
|
-
const tampered = buf.toString('base64');
|
|
27
|
-
expect(() => decryptSecret(tampered, key)).toThrow();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('rejects ciphertext encrypted under a different key', () => {
|
|
31
|
-
const otherKey = crypto.randomBytes(32);
|
|
32
|
-
const ct = encryptSecret('only-original-key', key);
|
|
33
|
-
expect(() => decryptSecret(ct, otherKey)).toThrow();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('handles empty strings', () => {
|
|
37
|
-
const ct = encryptSecret('', key);
|
|
38
|
-
expect(decryptSecret(ct, key)).toBe('');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('handles unicode', () => {
|
|
42
|
-
const v = '🔐 résumé — 秘密';
|
|
43
|
-
expect(decryptSecret(encryptSecret(v, key), key)).toBe(v);
|
|
44
|
-
});
|
|
45
|
-
});
|
package/src/secrets/crypto.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AES-256-GCM encryption for secret values.
|
|
3
|
-
*
|
|
4
|
-
* Wire format (base64-encoded):
|
|
5
|
-
* 12-byte IV || ciphertext || 16-byte auth tag
|
|
6
|
-
*
|
|
7
|
-
* Each call generates a fresh random IV — never reuse an IV with the same
|
|
8
|
-
* key (catastrophic for GCM). The auth tag is appended so decryption fails
|
|
9
|
-
* loudly on tampering.
|
|
10
|
-
*
|
|
11
|
-
* Domain separation: encryptSecret/decryptSecret accept a 32-byte key. Callers
|
|
12
|
-
* MUST NOT pass the raw master key — they pass a per-domain HKDF derivation
|
|
13
|
-
* (see `deriveKey` below). That way if a future subsystem (e.g. an outbox
|
|
14
|
-
* cookie signer) needs symmetric crypto from the same master, its key is
|
|
15
|
-
* cryptographically separated and a bug in one domain can't decrypt the other.
|
|
16
|
-
*/
|
|
17
|
-
import crypto from 'crypto';
|
|
18
|
-
|
|
19
|
-
const ALGO = 'aes-256-gcm';
|
|
20
|
-
const IV_LEN = 12;
|
|
21
|
-
const TAG_LEN = 16;
|
|
22
|
-
const KEY_LEN = 32;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* HKDF-SHA256 with an empty salt and a domain-specific `info` string. The
|
|
26
|
-
* empty salt is fine — the master key is already 256 bits of CSPRNG output,
|
|
27
|
-
* so HKDF degenerates to HKDF-Expand and the domain-tag in `info` does the
|
|
28
|
-
* real work. Use `paraclaw.<subsystem>.v<n>`; bumping `v` is a key rotation
|
|
29
|
-
* for that subsystem only.
|
|
30
|
-
*/
|
|
31
|
-
export function deriveKey(masterKey: Buffer, info: string): Buffer {
|
|
32
|
-
if (masterKey.length !== KEY_LEN) {
|
|
33
|
-
throw new Error(`master key must be ${KEY_LEN} bytes, got ${masterKey.length}`);
|
|
34
|
-
}
|
|
35
|
-
return Buffer.from(crypto.hkdfSync('sha256', masterKey, Buffer.alloc(0), info, KEY_LEN));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function encryptSecret(plaintext: string, key: Buffer): string {
|
|
39
|
-
const iv = crypto.randomBytes(IV_LEN);
|
|
40
|
-
const cipher = crypto.createCipheriv(ALGO, key, iv);
|
|
41
|
-
const ct = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
42
|
-
const tag = cipher.getAuthTag();
|
|
43
|
-
return Buffer.concat([iv, ct, tag]).toString('base64');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function decryptSecret(encoded: string, key: Buffer): string {
|
|
47
|
-
const buf = Buffer.from(encoded, 'base64');
|
|
48
|
-
if (buf.length < IV_LEN + TAG_LEN) throw new Error('ciphertext too short');
|
|
49
|
-
const iv = buf.subarray(0, IV_LEN);
|
|
50
|
-
const tag = buf.subarray(buf.length - TAG_LEN);
|
|
51
|
-
const ct = buf.subarray(IV_LEN, buf.length - TAG_LEN);
|
|
52
|
-
const decipher = crypto.createDecipheriv(ALGO, key, iv);
|
|
53
|
-
decipher.setAuthTag(tag);
|
|
54
|
-
return Buffer.concat([decipher.update(ct), decipher.final()]).toString('utf8');
|
|
55
|
-
}
|