auramaxx 1.0.0-alpha.4
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/LICENSE +26 -0
- package/README.md +112 -0
- package/bin/aurawallet.js +121 -0
- package/docs/ADAPTERS.md +467 -0
- package/docs/API.md +2679 -0
- package/docs/APPS.md +198 -0
- package/docs/ARCHITECTURE.md +350 -0
- package/docs/AUTH.md +698 -0
- package/docs/BEST-PRACTICES.md +121 -0
- package/docs/CLI.md +61 -0
- package/docs/DEVELOPING-APPS.md +452 -0
- package/docs/EXTENSION.md +97 -0
- package/docs/JOBS.md +33 -0
- package/docs/MCP.md +76 -0
- package/docs/PROTOCOL.md +142 -0
- package/docs/SETUP.md +219 -0
- package/docs/WORKSPACE.md +672 -0
- package/docs/agent-auth.md +63 -0
- package/docs/aura-file.md +48 -0
- package/docs/credentials.md +53 -0
- package/docs/external/getting-started.md +65 -0
- package/docs/external/overview.md +45 -0
- package/docs/external/use-cases.md +48 -0
- package/docs/external/why-aura.md +35 -0
- package/docs/jobs/connect-agent.md +77 -0
- package/docs/jobs/migrate-from-dotenv.md +79 -0
- package/docs/jobs/recover-from-lockout.md +72 -0
- package/docs/jobs/secure-ci.md +63 -0
- package/docs/oauth2.md +42 -0
- package/docs/passkeys.md +60 -0
- package/docs/security.md +540 -0
- package/docs/specs/aura-open-protocol.md +61 -0
- package/docs/specs/aura-provider-plugin.md +24 -0
- package/docs/specs/aura-registry-model.md +31 -0
- package/docs/specs/fixtures/invalid-bad-key.aura +1 -0
- package/docs/specs/fixtures/invalid-bad-unicode-escape.aura +1 -0
- package/docs/specs/fixtures/invalid-duplicate-key.aura +2 -0
- package/docs/specs/fixtures/valid-basic.aura +4 -0
- package/docs/specs/fixtures/valid-provider-ref.aura +1 -0
- package/docs/specs/fixtures/valid-quoted-escapes.aura +2 -0
- package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
- package/docs/totp.md +40 -0
- package/docs/wallet/AI.md +508 -0
- package/docs/wallet/DEVELOPING-STRATEGIES.md +713 -0
- package/docs/wallet/README.md +47 -0
- package/docs/wallet/STRATEGY.md +89 -0
- package/next.config.ts +21 -0
- package/package.json +151 -0
- package/postcss.config.mjs +8 -0
- package/prisma/migrations/20260214170000_baseline/migration.sql +511 -0
- package/prisma/migrations/20260216214537_add_passkey_model/migration.sql +18 -0
- package/prisma/migrations/20260217150500_add_credential_access_audit/migration.sql +31 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +447 -0
- package/public/logo-chevron.svg +31 -0
- package/public/logo-concentric.svg +31 -0
- package/public/logo-crosshatch.svg +39 -0
- package/public/logo-dashed.svg +39 -0
- package/public/logo-horizontal.svg +31 -0
- package/public/logo-m56.svg +64 -0
- package/public/logo.webp +0 -0
- package/scripts/add-app.js +245 -0
- package/scripts/init.sh +57 -0
- package/scripts/migrate-apikeys-to-credentials.ts +35 -0
- package/scripts/sandbox-agent-flow.sh +235 -0
- package/scripts/sandbox.sh +175 -0
- package/scripts/validate-job-docs.mjs +125 -0
- package/server/abi/SwapHelper.json +438 -0
- package/server/cli/approval.ts +447 -0
- package/server/cli/commands/app.ts +204 -0
- package/server/cli/commands/cron.ts +24 -0
- package/server/cli/commands/doctor.ts +1007 -0
- package/server/cli/commands/env.ts +456 -0
- package/server/cli/commands/init.ts +752 -0
- package/server/cli/commands/mcp.ts +125 -0
- package/server/cli/commands/restore.ts +314 -0
- package/server/cli/commands/shell-hook.ts +468 -0
- package/server/cli/commands/start.ts +62 -0
- package/server/cli/commands/status.ts +59 -0
- package/server/cli/commands/stop.ts +14 -0
- package/server/cli/commands/token.ts +180 -0
- package/server/cli/commands/unlock.ts +49 -0
- package/server/cli/commands/vault.ts +417 -0
- package/server/cli/index.ts +328 -0
- package/server/cli/lib/aura-parser.ts +64 -0
- package/server/cli/lib/credential-create.ts +74 -0
- package/server/cli/lib/credential-resolve.ts +254 -0
- package/server/cli/lib/dotenv-migrate.ts +116 -0
- package/server/cli/lib/dotenv-parser.ts +146 -0
- package/server/cli/lib/http.ts +91 -0
- package/server/cli/lib/init-steps.ts +76 -0
- package/server/cli/lib/local-agent-trust.ts +45 -0
- package/server/cli/lib/process.ts +136 -0
- package/server/cli/lib/prompt.ts +85 -0
- package/server/cli/lib/theme.ts +240 -0
- package/server/cli/socket.ts +570 -0
- package/server/cli/transport-client.ts +50 -0
- package/server/cron/index.ts +137 -0
- package/server/cron/job.ts +31 -0
- package/server/cron/jobs/balance-sync.ts +436 -0
- package/server/cron/jobs/incoming-scan.ts +506 -0
- package/server/cron/jobs/native-price.ts +70 -0
- package/server/cron/jobs/orphan-cleanup.ts +40 -0
- package/server/cron/jobs/strategy-runner.ts +175 -0
- package/server/cron/scheduler.ts +125 -0
- package/server/index.ts +406 -0
- package/server/lib/adapters/factory.ts +110 -0
- package/server/lib/adapters/index.ts +19 -0
- package/server/lib/adapters/router.ts +297 -0
- package/server/lib/adapters/telegram.ts +645 -0
- package/server/lib/adapters/types.ts +89 -0
- package/server/lib/adapters/webhook.ts +95 -0
- package/server/lib/address.ts +49 -0
- package/server/lib/agent-auth/contracts.ts +1194 -0
- package/server/lib/agent-profiles.ts +328 -0
- package/server/lib/ai.ts +285 -0
- package/server/lib/api-registry/contracts.ts +86 -0
- package/server/lib/api-registry/validation.ts +172 -0
- package/server/lib/apikey-migration.ts +189 -0
- package/server/lib/app-installer.ts +505 -0
- package/server/lib/app-tokens.ts +247 -0
- package/server/lib/auth.ts +314 -0
- package/server/lib/batch.ts +242 -0
- package/server/lib/cold.ts +874 -0
- package/server/lib/config.ts +381 -0
- package/server/lib/credential-access-audit.ts +85 -0
- package/server/lib/credential-access-policy.ts +110 -0
- package/server/lib/credential-health.ts +343 -0
- package/server/lib/credential-import.ts +487 -0
- package/server/lib/credential-scope.ts +87 -0
- package/server/lib/credential-shares.ts +190 -0
- package/server/lib/credential-transport.ts +342 -0
- package/server/lib/credential-vault.ts +77 -0
- package/server/lib/credentials.ts +333 -0
- package/server/lib/crypto.ts +8 -0
- package/server/lib/db.ts +15 -0
- package/server/lib/defaults.ts +366 -0
- package/server/lib/dex/index.ts +80 -0
- package/server/lib/dex/relay.ts +235 -0
- package/server/lib/dex/types.ts +59 -0
- package/server/lib/dex/uniswap.ts +370 -0
- package/server/lib/e2e-agent/artifacts.ts +36 -0
- package/server/lib/e2e-agent/contracts.ts +112 -0
- package/server/lib/e2e-agent/validation.ts +135 -0
- package/server/lib/encrypt.ts +128 -0
- package/server/lib/error.ts +20 -0
- package/server/lib/events.ts +205 -0
- package/server/lib/hot.ts +357 -0
- package/server/lib/key-fingerprint.ts +28 -0
- package/server/lib/logger.ts +331 -0
- package/server/lib/network.ts +137 -0
- package/server/lib/notifications.ts +219 -0
- package/server/lib/oauth2-refresh.ts +241 -0
- package/server/lib/oursecret.ts +54 -0
- package/server/lib/passkey-credential.ts +360 -0
- package/server/lib/passkey.ts +68 -0
- package/server/lib/permissions.ts +248 -0
- package/server/lib/pino.ts +24 -0
- package/server/lib/policy-preview.ts +138 -0
- package/server/lib/price.ts +338 -0
- package/server/lib/prices.ts +34 -0
- package/server/lib/project-scope.ts +239 -0
- package/server/lib/resolve-action.ts +427 -0
- package/server/lib/resolve.ts +36 -0
- package/server/lib/sessions.ts +632 -0
- package/server/lib/solana/connection.ts +26 -0
- package/server/lib/solana/jupiter.ts +128 -0
- package/server/lib/solana/transfer.ts +108 -0
- package/server/lib/solana/wallet.ts +136 -0
- package/server/lib/strategy/emits.ts +21 -0
- package/server/lib/strategy/engine.ts +1305 -0
- package/server/lib/strategy/executor.ts +115 -0
- package/server/lib/strategy/hook-context.ts +158 -0
- package/server/lib/strategy/hooks.ts +990 -0
- package/server/lib/strategy/index.ts +28 -0
- package/server/lib/strategy/installer.ts +305 -0
- package/server/lib/strategy/loader.ts +256 -0
- package/server/lib/strategy/message.ts +235 -0
- package/server/lib/strategy/repository.ts +218 -0
- package/server/lib/strategy/session-logger.ts +693 -0
- package/server/lib/strategy/sources.ts +288 -0
- package/server/lib/strategy/state.ts +189 -0
- package/server/lib/strategy/templates.ts +403 -0
- package/server/lib/strategy/tick.ts +404 -0
- package/server/lib/strategy/types.ts +230 -0
- package/server/lib/swap.ts +3 -0
- package/server/lib/temp.ts +86 -0
- package/server/lib/token-metadata.ts +86 -0
- package/server/lib/token-safety.ts +200 -0
- package/server/lib/token-search.ts +444 -0
- package/server/lib/totp.ts +194 -0
- package/server/lib/transactions.ts +123 -0
- package/server/lib/transport.ts +75 -0
- package/server/lib/txhistory/decoder.ts +262 -0
- package/server/lib/txhistory/enricher.ts +652 -0
- package/server/lib/txhistory/index.ts +391 -0
- package/server/lib/txhistory/signatures.ts +59 -0
- package/server/lib/verified-summary.ts +421 -0
- package/server/mcp/profile-policy.ts +30 -0
- package/server/mcp/server.ts +619 -0
- package/server/mcp/tools.ts +523 -0
- package/server/middleware/auth.ts +119 -0
- package/server/middleware/requestLogger.ts +84 -0
- package/server/routes/actions.ts +459 -0
- package/server/routes/adapters.ts +703 -0
- package/server/routes/addressbook.ts +113 -0
- package/server/routes/ai.ts +34 -0
- package/server/routes/apikeys.ts +295 -0
- package/server/routes/apps.ts +601 -0
- package/server/routes/auth.ts +457 -0
- package/server/routes/backup.ts +340 -0
- package/server/routes/batch.ts +270 -0
- package/server/routes/bookmarks.ts +162 -0
- package/server/routes/credential-shares.ts +198 -0
- package/server/routes/credential-vaults.ts +154 -0
- package/server/routes/credentials.ts +1290 -0
- package/server/routes/dashboard.ts +71 -0
- package/server/routes/defaults.ts +124 -0
- package/server/routes/fund.ts +229 -0
- package/server/routes/import.ts +352 -0
- package/server/routes/launch.ts +665 -0
- package/server/routes/lock.ts +54 -0
- package/server/routes/logs.ts +68 -0
- package/server/routes/nuke.ts +111 -0
- package/server/routes/passkey-credentials.ts +99 -0
- package/server/routes/passkey.ts +346 -0
- package/server/routes/portfolio.ts +217 -0
- package/server/routes/price.ts +63 -0
- package/server/routes/resolve.ts +31 -0
- package/server/routes/security.ts +45 -0
- package/server/routes/send-evm.ts +241 -0
- package/server/routes/send-solana.ts +281 -0
- package/server/routes/send.ts +178 -0
- package/server/routes/setup.ts +210 -0
- package/server/routes/strategy.ts +894 -0
- package/server/routes/swap-evm.ts +353 -0
- package/server/routes/swap-solana.ts +177 -0
- package/server/routes/swap.ts +356 -0
- package/server/routes/token.ts +247 -0
- package/server/routes/unlock.ts +403 -0
- package/server/routes/wallet-assets.ts +361 -0
- package/server/routes/wallet-transactions.ts +515 -0
- package/server/routes/wallet.ts +710 -0
- package/server/types.ts +146 -0
- package/skills/aurawallet/SKILL.md +739 -0
- package/skills/aurawallet-setup/SKILL.md +74 -0
- package/skills/security-review/SKILL.md +148 -0
- package/src/app/api/agent-requests/route.ts +30 -0
- package/src/app/api/apps/install/route.ts +126 -0
- package/src/app/api/apps/manifests/route.ts +16 -0
- package/src/app/api/apps/static/[...path]/route.ts +57 -0
- package/src/app/api/events/route.ts +92 -0
- package/src/app/api/page.tsx +212 -0
- package/src/app/api/workspace/[id]/apps/[wid]/route.ts +119 -0
- package/src/app/api/workspace/[id]/apps/route.ts +81 -0
- package/src/app/api/workspace/[id]/export/route.ts +67 -0
- package/src/app/api/workspace/[id]/route.ts +168 -0
- package/src/app/api/workspace/auth.ts +34 -0
- package/src/app/api/workspace/config/route.ts +106 -0
- package/src/app/api/workspace/import/route.ts +127 -0
- package/src/app/api/workspace/route.ts +116 -0
- package/src/app/app/page.tsx +2122 -0
- package/src/app/apple-icon.png +0 -0
- package/src/app/docs/page.tsx +178 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +572 -0
- package/src/app/health/page.tsx +5 -0
- package/src/app/hello/page.tsx +15 -0
- package/src/app/icon.png +0 -0
- package/src/app/layout.tsx +34 -0
- package/src/app/page.tsx +986 -0
- package/src/app/providers.tsx +90 -0
- package/src/app/share/[token]/page.tsx +295 -0
- package/src/components/ChainSelector.tsx +144 -0
- package/src/components/HumanActionBar.tsx +695 -0
- package/src/components/NotificationDrawer.tsx +129 -0
- package/src/components/apps/AgentKeysApp.tsx +490 -0
- package/src/components/apps/App.tsx +153 -0
- package/src/components/apps/AppGrid.tsx +15 -0
- package/src/components/apps/DetailedAddressDrawer.tsx +325 -0
- package/src/components/apps/DraggableApp.tsx +562 -0
- package/src/components/apps/IFrameApp.tsx +73 -0
- package/src/components/apps/LogsApp.tsx +360 -0
- package/src/components/apps/SendApp.tsx +394 -0
- package/src/components/apps/SetupWizardApp.tsx +1004 -0
- package/src/components/apps/SystemDefaultsApp.tsx +845 -0
- package/src/components/apps/ThirdPartyApp.tsx +428 -0
- package/src/components/apps/TokenApp.tsx +319 -0
- package/src/components/apps/TransactionsApp.tsx +438 -0
- package/src/components/apps/WalletDetailApp.tsx +1505 -0
- package/src/components/apps/index.ts +13 -0
- package/src/components/design-system/Button.tsx +53 -0
- package/src/components/design-system/ChainIndicator.tsx +65 -0
- package/src/components/design-system/ChainSelector.tsx +137 -0
- package/src/components/design-system/ConfirmationModal.tsx +106 -0
- package/src/components/design-system/ConfirmationPopover.tsx +81 -0
- package/src/components/design-system/Drawer.tsx +123 -0
- package/src/components/design-system/FilterDropdown.tsx +72 -0
- package/src/components/design-system/Modal.tsx +206 -0
- package/src/components/design-system/Popover.tsx +142 -0
- package/src/components/design-system/TextInput.tsx +85 -0
- package/src/components/design-system/Toggle.tsx +58 -0
- package/src/components/design-system/index.ts +11 -0
- package/src/components/docs/DocsThemeToggle.tsx +49 -0
- package/src/components/health/CredentialHealthDashboard.tsx +214 -0
- package/src/components/icons/ChainIcons.tsx +72 -0
- package/src/components/layout/AppStoreDrawer.tsx +369 -0
- package/src/components/layout/ContentArea.tsx +21 -0
- package/src/components/layout/TabBar.tsx +278 -0
- package/src/components/layout/WalletSidebar.tsx +1033 -0
- package/src/components/layout/index.ts +4 -0
- package/src/components/marketing/AuraWalletSpecOverlay.tsx +635 -0
- package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
- package/src/components/vault/ApiKeysConsole.tsx +1080 -0
- package/src/components/vault/AuditConsole.tsx +584 -0
- package/src/components/vault/CredentialDetail.tsx +455 -0
- package/src/components/vault/CredentialEmpty.tsx +55 -0
- package/src/components/vault/CredentialField.tsx +361 -0
- package/src/components/vault/CredentialForm.tsx +1212 -0
- package/src/components/vault/CredentialList.tsx +165 -0
- package/src/components/vault/CredentialRow.tsx +97 -0
- package/src/components/vault/CredentialShareModal.tsx +178 -0
- package/src/components/vault/CredentialVault.tsx +754 -0
- package/src/components/vault/CredentialWalletWidget.tsx +103 -0
- package/src/components/vault/ImportCredentialsModal.tsx +515 -0
- package/src/components/vault/LargeTypeModal.tsx +64 -0
- package/src/components/vault/PasswordGenerator.tsx +224 -0
- package/src/components/vault/TOTPDisplay.tsx +123 -0
- package/src/components/vault/VaultSidebar.tsx +413 -0
- package/src/components/vault/types.ts +54 -0
- package/src/context/AuthContext.tsx +337 -0
- package/src/context/PriceContext.tsx +113 -0
- package/src/context/ThemeContext.tsx +164 -0
- package/src/context/WebSocketContext.tsx +269 -0
- package/src/context/WorkspaceContext.tsx +668 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useAgentActions.ts +368 -0
- package/src/hooks/useBalance.ts +103 -0
- package/src/hooks/useBalances.ts +129 -0
- package/src/instrumentation.ts +12 -0
- package/src/lib/api.ts +449 -0
- package/src/lib/app-loader.ts +148 -0
- package/src/lib/app-registry.ts +178 -0
- package/src/lib/app-sdk.ts +157 -0
- package/src/lib/audit-console-adapter.ts +151 -0
- package/src/lib/auth-client.ts +75 -0
- package/src/lib/config.ts +74 -0
- package/src/lib/crypto.ts +112 -0
- package/src/lib/db.ts +21 -0
- package/src/lib/docs.ts +390 -0
- package/src/lib/events.ts +361 -0
- package/src/lib/pino.ts +24 -0
- package/src/lib/theme-handlers.ts +168 -0
- package/src/lib/theme.ts +351 -0
- package/src/lib/tokenData.ts +378 -0
- package/src/lib/vault-crypto.ts +129 -0
- package/src/lib/websocket-server.ts +302 -0
- package/src/lib/websocket-setup.ts +79 -0
- package/src/lib/wordlist.ts +2050 -0
- package/src/lib/workspace-handlers.ts +285 -0
- package/start.sh +80 -0
- package/tailwind.config.ts +99 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Handler
|
|
3
|
+
* ===============
|
|
4
|
+
* Processes human-to-app messages through the AI message hook.
|
|
5
|
+
* Serial per-app (prevents state race conditions), parallel across apps.
|
|
6
|
+
* Rate limited: 10 messages per 60s per app.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { StrategyManifest, MessageContext } from './types';
|
|
10
|
+
import { callHook } from './hooks';
|
|
11
|
+
import { getState, updateState, getConfigOverrides, persistState, restoreState } from './state';
|
|
12
|
+
import { getTokenHash } from '../auth';
|
|
13
|
+
import { getSessionBudget } from '../sessions';
|
|
14
|
+
import { processEmits } from './emits';
|
|
15
|
+
import { getDefaultSync, parseRateLimit } from '../defaults';
|
|
16
|
+
import { log } from '../pino';
|
|
17
|
+
import { getErrorMessage } from '../error';
|
|
18
|
+
import {
|
|
19
|
+
startChatSession,
|
|
20
|
+
logReply as sessionLogReply,
|
|
21
|
+
logToolCall as sessionLogToolCall,
|
|
22
|
+
logError as sessionLogError,
|
|
23
|
+
endChatSession,
|
|
24
|
+
type AdapterSource,
|
|
25
|
+
} from './session-logger';
|
|
26
|
+
|
|
27
|
+
const bypassRateLimit = process.env.BYPASS_RATE_LIMIT === 'true';
|
|
28
|
+
|
|
29
|
+
/** Per-app serial queue */
|
|
30
|
+
const queues = new Map<string, Promise<unknown>>();
|
|
31
|
+
|
|
32
|
+
/** Per-app rate limit tracking */
|
|
33
|
+
const rateLimits = new Map<string, number[]>();
|
|
34
|
+
|
|
35
|
+
interface MessageRequest {
|
|
36
|
+
appId: string;
|
|
37
|
+
message: string;
|
|
38
|
+
onProgress?: (status: string) => void;
|
|
39
|
+
adapter?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface MessageRuntime {
|
|
43
|
+
manifest: StrategyManifest;
|
|
44
|
+
token?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Detect ticker/name lookups that should always use /token/search first. */
|
|
48
|
+
function isTickerLookup(message: string): boolean {
|
|
49
|
+
const lower = message.toLowerCase();
|
|
50
|
+
const hasTickerSymbol = /\$[a-z0-9]{2,}/i.test(message);
|
|
51
|
+
const hasTokenIntent = /\b(token|ticker|market cap|mcap|price|liquidity|volume|fdv)\b/.test(lower);
|
|
52
|
+
const hasContractAddress = /0x[a-fA-F0-9]{6,}/.test(message);
|
|
53
|
+
return !hasContractAddress && (hasTickerSymbol || hasTokenIntent);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Guardrail: if the model deflects a ticker lookup to external sites without
|
|
58
|
+
* using tools, force one retry with explicit /token/search instruction.
|
|
59
|
+
*/
|
|
60
|
+
function shouldRetryTickerLookup(
|
|
61
|
+
message: string,
|
|
62
|
+
reply: string | null,
|
|
63
|
+
toolCallCount?: number,
|
|
64
|
+
): boolean {
|
|
65
|
+
if (!isTickerLookup(message) || !reply) return false;
|
|
66
|
+
if ((toolCallCount || 0) > 0) return false;
|
|
67
|
+
|
|
68
|
+
const lower = reply.toLowerCase();
|
|
69
|
+
const externalRedirect = /\b(coingecko|coinmarketcap|dextools|dexscreener|basescan)\b/.test(lower);
|
|
70
|
+
const claimsUnavailable = /\b(don't have|do not have|can't|cannot|unable)\b/.test(lower)
|
|
71
|
+
&& /\b(search|market cap|data)\b/.test(lower);
|
|
72
|
+
const asksForAddressFirst = /\b(need|provide|share)\b.{0,60}\b(contract address|0x)\b/.test(lower);
|
|
73
|
+
const mentionsSearchUnavailable = /\b(no|not)\b.{0,40}\b(token search|search endpoint)\b/.test(lower);
|
|
74
|
+
|
|
75
|
+
return externalRedirect || claimsUnavailable || asksForAddressFirst || mentionsSearchUnavailable;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Queue a message for processing. Serial per-app, parallel across apps.
|
|
80
|
+
* Returns the reply (or null) once processing completes.
|
|
81
|
+
*/
|
|
82
|
+
export function processMessage(
|
|
83
|
+
req: MessageRequest,
|
|
84
|
+
runtime: MessageRuntime,
|
|
85
|
+
): Promise<{ reply: string | null; error?: string }> {
|
|
86
|
+
const prev = queues.get(req.appId) || Promise.resolve();
|
|
87
|
+
const next = prev.then(() => handleMessage(req, runtime)).catch((err) => {
|
|
88
|
+
const msg = getErrorMessage(err);
|
|
89
|
+
return { reply: null, error: msg };
|
|
90
|
+
});
|
|
91
|
+
queues.set(req.appId, next);
|
|
92
|
+
return next;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Process a single message: rate limit check, build context, call hook, update state.
|
|
97
|
+
*/
|
|
98
|
+
async function handleMessage(
|
|
99
|
+
req: MessageRequest,
|
|
100
|
+
runtime: MessageRuntime,
|
|
101
|
+
): Promise<{ reply: string | null; error?: string }> {
|
|
102
|
+
const { appId, message } = req;
|
|
103
|
+
const { manifest } = runtime;
|
|
104
|
+
const tag = `[strategy:${appId}]`;
|
|
105
|
+
|
|
106
|
+
// Rate limit check (bypassed in dev mode)
|
|
107
|
+
if (!bypassRateLimit) {
|
|
108
|
+
const { max: RATE_LIMIT, windowMs: RATE_WINDOW_MS } = parseRateLimit(getDefaultSync('rate.app_message', '10,60000'));
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
const timestamps = rateLimits.get(appId) || [];
|
|
111
|
+
const recent = timestamps.filter(t => now - t < RATE_WINDOW_MS);
|
|
112
|
+
if (recent.length >= RATE_LIMIT) {
|
|
113
|
+
console.warn(`${tag} message rate limited (${recent.length}/${RATE_LIMIT} in ${RATE_WINDOW_MS / 1000}s)`);
|
|
114
|
+
return { reply: null, error: 'Rate limited — too many messages' };
|
|
115
|
+
}
|
|
116
|
+
recent.push(now);
|
|
117
|
+
rateLimits.set(appId, recent);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Build context
|
|
121
|
+
try {
|
|
122
|
+
await restoreState(appId);
|
|
123
|
+
} catch (err) {
|
|
124
|
+
log.warn({ err, appId }, 'state restore failed before message');
|
|
125
|
+
}
|
|
126
|
+
const state = getState(appId);
|
|
127
|
+
const configOverrides = await getConfigOverrides(appId);
|
|
128
|
+
const config = { ...manifest.config, ...configOverrides };
|
|
129
|
+
|
|
130
|
+
const context: MessageContext = {
|
|
131
|
+
message,
|
|
132
|
+
appId,
|
|
133
|
+
state,
|
|
134
|
+
config,
|
|
135
|
+
permissions: manifest.permissions,
|
|
136
|
+
budget: runtime.token ? getSessionBudget(getTokenHash(runtime.token)) : { limits: {}, spent: {}, remaining: {} },
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
console.log(`${tag} message → "${message.slice(0, 100)}${message.length > 100 ? '...' : ''}"`);
|
|
140
|
+
|
|
141
|
+
// Start session logging
|
|
142
|
+
const sessionId = startChatSession(appId, (req.adapter || 'unknown') as AdapterSource, message);
|
|
143
|
+
const hookStart = Date.now();
|
|
144
|
+
|
|
145
|
+
// Tool call callback for session logging
|
|
146
|
+
const onToolCall = (entry: { name: string; input: Record<string, unknown>; result: string; durationMs: number }) => {
|
|
147
|
+
sessionLogToolCall(sessionId, entry);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
// Call message hook
|
|
152
|
+
let result = await callHook(manifest, 'message', context, runtime.token, req.onProgress, onToolCall);
|
|
153
|
+
|
|
154
|
+
const initialReply = result.reply || result.log || null;
|
|
155
|
+
if (shouldRetryTickerLookup(message, initialReply, result._meta?.toolCallCount)) {
|
|
156
|
+
const retryContext: MessageContext & {
|
|
157
|
+
retry?: { reason: string; requiredToolCall: string };
|
|
158
|
+
} = {
|
|
159
|
+
...context,
|
|
160
|
+
retry: {
|
|
161
|
+
reason: 'Ticker/name lookup response skipped token search guardrail.',
|
|
162
|
+
requiredToolCall: 'wallet_api GET /token/search?q=<token>&chain=<chain> before asking for a contract address or external website.',
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
result = await callHook(manifest, 'message', retryContext, runtime.token, req.onProgress, onToolCall);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const durationMs = Date.now() - hookStart;
|
|
169
|
+
|
|
170
|
+
// Update state if the hook returned state changes
|
|
171
|
+
if (result.state && Object.keys(result.state).length > 0) {
|
|
172
|
+
updateState(appId, result.state);
|
|
173
|
+
await persistState(appId).catch((err) => {
|
|
174
|
+
log.warn({ err, appId }, 'state persistence failed after message');
|
|
175
|
+
});
|
|
176
|
+
console.log(`${tag} message state updated: ${JSON.stringify(result.state).slice(0, 200)}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Broadcast any emit events from the message hook
|
|
180
|
+
processEmits(appId, result);
|
|
181
|
+
|
|
182
|
+
// Process intents if any (dynamic import to avoid circular dependency with engine)
|
|
183
|
+
if (result.intents && result.intents.length > 0) {
|
|
184
|
+
console.log(`${tag} message produced ${result.intents.length} intent(s)`);
|
|
185
|
+
try {
|
|
186
|
+
const { processIntents } = await import('./tick');
|
|
187
|
+
await processIntents(manifest, result.intents, config, runtime.token, 0);
|
|
188
|
+
await persistState(appId).catch((err) => {
|
|
189
|
+
log.warn({ err, appId }, 'state persistence failed after message intents');
|
|
190
|
+
});
|
|
191
|
+
} catch (err) {
|
|
192
|
+
console.error(`${tag} message intent processing error:`, err);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Extract reply: prefer reply field, fall back to log
|
|
197
|
+
const reply = result.reply || result.log || null;
|
|
198
|
+
console.log(`${tag} message ← reply: ${reply ? reply.slice(0, 200) : '(none)'}`);
|
|
199
|
+
|
|
200
|
+
// Log reply with metadata from the AI provider (model, tokens, timing)
|
|
201
|
+
const meta = result._meta;
|
|
202
|
+
sessionLogReply(sessionId, reply, {
|
|
203
|
+
model: meta?.model,
|
|
204
|
+
tokens: meta?.tokens ? meta.tokens.input + meta.tokens.output : undefined,
|
|
205
|
+
durationMs,
|
|
206
|
+
provider: meta?.provider,
|
|
207
|
+
costUsd: meta?.costUsd,
|
|
208
|
+
inputTokens: meta?.tokens?.input,
|
|
209
|
+
outputTokens: meta?.tokens?.output,
|
|
210
|
+
cacheReadTokens: meta?.tokens?.cacheRead,
|
|
211
|
+
toolCallCount: meta?.toolCallCount,
|
|
212
|
+
});
|
|
213
|
+
// Don't end session here — it stays alive for idle-timeout grouping.
|
|
214
|
+
// Sessions are closed when: (a) idle timeout expires on next startChatSession(),
|
|
215
|
+
// (b) graceful shutdown via endAllActiveSessions(), or (c) crash recovery.
|
|
216
|
+
|
|
217
|
+
return { reply };
|
|
218
|
+
} catch (err) {
|
|
219
|
+
sessionLogError(sessionId, err);
|
|
220
|
+
endChatSession(sessionId, 'error');
|
|
221
|
+
throw err;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Clear the message queue for a app (e.g. on disable) */
|
|
226
|
+
export function clearMessageQueue(appId: string): void {
|
|
227
|
+
queues.delete(appId);
|
|
228
|
+
rateLimits.delete(appId);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Clear all message queues (e.g. on engine shutdown) */
|
|
232
|
+
export function clearAllMessageQueues(): void {
|
|
233
|
+
queues.clear();
|
|
234
|
+
rateLimits.clear();
|
|
235
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { prisma } from '../db';
|
|
2
|
+
import type { StrategyManifest } from './types';
|
|
3
|
+
|
|
4
|
+
export interface PersistedStrategy {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
templateId: string | null;
|
|
8
|
+
mode: string;
|
|
9
|
+
manifest: StrategyManifest;
|
|
10
|
+
config: Record<string, unknown>;
|
|
11
|
+
state: Record<string, unknown>;
|
|
12
|
+
schedule: Record<string, unknown>;
|
|
13
|
+
permissions: string[];
|
|
14
|
+
limits: { fund?: number; send?: number } | null;
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
status: string;
|
|
17
|
+
createdBy: string;
|
|
18
|
+
provenance: Record<string, unknown> | null;
|
|
19
|
+
appId: string | null;
|
|
20
|
+
lastTickAt: Date | null;
|
|
21
|
+
lastError: string | null;
|
|
22
|
+
errorCount: number;
|
|
23
|
+
createdAt: Date;
|
|
24
|
+
updatedAt: Date;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface CreatePersistedStrategyInput {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
templateId?: string | null;
|
|
31
|
+
mode?: string;
|
|
32
|
+
manifest: StrategyManifest;
|
|
33
|
+
config?: Record<string, unknown>;
|
|
34
|
+
state?: Record<string, unknown>;
|
|
35
|
+
schedule?: Record<string, unknown>;
|
|
36
|
+
permissions?: string[];
|
|
37
|
+
limits?: { fund?: number; send?: number } | null;
|
|
38
|
+
enabled?: boolean;
|
|
39
|
+
status?: string;
|
|
40
|
+
createdBy?: string;
|
|
41
|
+
provenance?: Record<string, unknown> | null;
|
|
42
|
+
appId?: string | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseObject(value: string | null | undefined): Record<string, unknown> {
|
|
46
|
+
if (!value) return {};
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(value);
|
|
49
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
50
|
+
return parsed as Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
return {};
|
|
53
|
+
} catch {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function parseArray(value: string | null | undefined): string[] {
|
|
59
|
+
if (!value) return [];
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(value);
|
|
62
|
+
return Array.isArray(parsed) ? parsed.filter((entry): entry is string => typeof entry === 'string') : [];
|
|
63
|
+
} catch {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function parseLimits(value: string | null | undefined): { fund?: number; send?: number } | null {
|
|
69
|
+
if (!value) return null;
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(value) as { fund?: unknown; send?: unknown };
|
|
72
|
+
const next: { fund?: number; send?: number } = {};
|
|
73
|
+
if (typeof parsed.fund === 'number') next.fund = parsed.fund;
|
|
74
|
+
if (typeof parsed.send === 'number') next.send = parsed.send;
|
|
75
|
+
return Object.keys(next).length > 0 ? next : null;
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function deserializeStrategy(row: {
|
|
82
|
+
id: string;
|
|
83
|
+
name: string;
|
|
84
|
+
templateId: string | null;
|
|
85
|
+
mode: string;
|
|
86
|
+
manifest: string;
|
|
87
|
+
config: string | null;
|
|
88
|
+
state: string | null;
|
|
89
|
+
schedule: string | null;
|
|
90
|
+
permissions: string;
|
|
91
|
+
limits: string | null;
|
|
92
|
+
enabled: boolean;
|
|
93
|
+
status: string;
|
|
94
|
+
createdBy: string;
|
|
95
|
+
provenance: string | null;
|
|
96
|
+
appId: string | null;
|
|
97
|
+
lastTickAt: Date | null;
|
|
98
|
+
lastError: string | null;
|
|
99
|
+
errorCount: number;
|
|
100
|
+
createdAt: Date;
|
|
101
|
+
updatedAt: Date;
|
|
102
|
+
}): PersistedStrategy {
|
|
103
|
+
const manifestParsed = parseObject(row.manifest) as unknown as StrategyManifest;
|
|
104
|
+
return {
|
|
105
|
+
id: row.id,
|
|
106
|
+
name: row.name,
|
|
107
|
+
templateId: row.templateId,
|
|
108
|
+
mode: row.mode,
|
|
109
|
+
manifest: manifestParsed,
|
|
110
|
+
config: parseObject(row.config),
|
|
111
|
+
state: parseObject(row.state),
|
|
112
|
+
schedule: parseObject(row.schedule),
|
|
113
|
+
permissions: parseArray(row.permissions),
|
|
114
|
+
limits: parseLimits(row.limits),
|
|
115
|
+
enabled: row.enabled,
|
|
116
|
+
status: row.status,
|
|
117
|
+
createdBy: row.createdBy,
|
|
118
|
+
provenance: row.provenance ? parseObject(row.provenance) : null,
|
|
119
|
+
appId: row.appId,
|
|
120
|
+
lastTickAt: row.lastTickAt,
|
|
121
|
+
lastError: row.lastError,
|
|
122
|
+
errorCount: row.errorCount,
|
|
123
|
+
createdAt: row.createdAt,
|
|
124
|
+
updatedAt: row.updatedAt,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export async function listPersistedStrategies(): Promise<PersistedStrategy[]> {
|
|
129
|
+
const rows = await prisma.strategy.findMany({
|
|
130
|
+
orderBy: { createdAt: 'desc' },
|
|
131
|
+
});
|
|
132
|
+
return rows.map(deserializeStrategy).filter((strategy) => {
|
|
133
|
+
const m = strategy.manifest as Record<string, unknown>;
|
|
134
|
+
if (!m.id || typeof m.id !== 'string' ||
|
|
135
|
+
!m.name || typeof m.name !== 'string' ||
|
|
136
|
+
!m.hooks || typeof m.hooks !== 'object') {
|
|
137
|
+
console.warn(`[strategy:${strategy.id}] skipping — invalid manifest (missing id, name, or hooks)`);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function getPersistedStrategy(id: string): Promise<PersistedStrategy | null> {
|
|
145
|
+
const row = await prisma.strategy.findUnique({
|
|
146
|
+
where: { id },
|
|
147
|
+
});
|
|
148
|
+
return row ? deserializeStrategy(row) : null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function createPersistedStrategy(input: CreatePersistedStrategyInput): Promise<PersistedStrategy> {
|
|
152
|
+
const row = await prisma.strategy.create({
|
|
153
|
+
data: {
|
|
154
|
+
id: input.id,
|
|
155
|
+
name: input.name,
|
|
156
|
+
templateId: input.templateId ?? null,
|
|
157
|
+
mode: input.mode ?? 'headless',
|
|
158
|
+
manifest: JSON.stringify(input.manifest),
|
|
159
|
+
config: JSON.stringify(input.config ?? {}),
|
|
160
|
+
state: JSON.stringify(input.state ?? {}),
|
|
161
|
+
schedule: JSON.stringify(input.schedule ?? {}),
|
|
162
|
+
permissions: JSON.stringify(input.permissions ?? []),
|
|
163
|
+
limits: input.limits ? JSON.stringify(input.limits) : null,
|
|
164
|
+
enabled: input.enabled ?? false,
|
|
165
|
+
status: input.status ?? 'draft',
|
|
166
|
+
createdBy: input.createdBy ?? 'human',
|
|
167
|
+
provenance: input.provenance ? JSON.stringify(input.provenance) : null,
|
|
168
|
+
appId: input.appId ?? null,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
return deserializeStrategy(row);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function updatePersistedStrategyEnabled(id: string, enabled: boolean): Promise<PersistedStrategy | null> {
|
|
175
|
+
try {
|
|
176
|
+
const row = await prisma.strategy.update({
|
|
177
|
+
where: { id },
|
|
178
|
+
data: {
|
|
179
|
+
enabled,
|
|
180
|
+
status: enabled ? 'enabled' : 'disabled',
|
|
181
|
+
lastError: null,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
return deserializeStrategy(row);
|
|
185
|
+
} catch {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export async function updatePersistedStrategyConfig(
|
|
191
|
+
id: string,
|
|
192
|
+
config: Record<string, unknown>,
|
|
193
|
+
): Promise<PersistedStrategy | null> {
|
|
194
|
+
try {
|
|
195
|
+
const row = await prisma.strategy.update({
|
|
196
|
+
where: { id },
|
|
197
|
+
data: { config: JSON.stringify(config) },
|
|
198
|
+
});
|
|
199
|
+
return deserializeStrategy(row);
|
|
200
|
+
} catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export async function updatePersistedStrategyState(
|
|
206
|
+
id: string,
|
|
207
|
+
state: Record<string, unknown>,
|
|
208
|
+
): Promise<PersistedStrategy | null> {
|
|
209
|
+
try {
|
|
210
|
+
const row = await prisma.strategy.update({
|
|
211
|
+
where: { id },
|
|
212
|
+
data: { state: JSON.stringify(state) },
|
|
213
|
+
});
|
|
214
|
+
return deserializeStrategy(row);
|
|
215
|
+
} catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|