mercury-agent 0.4.5
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 +22 -0
- package/README.md +438 -0
- package/container/Dockerfile +127 -0
- package/container/Dockerfile.base +109 -0
- package/container/Dockerfile.power +17 -0
- package/container/agent-package.json +8 -0
- package/container/build.sh +54 -0
- package/docs/TODOS.md +147 -0
- package/docs/auth/dashboard.md +28 -0
- package/docs/auth/overview.md +109 -0
- package/docs/auth/whatsapp.md +173 -0
- package/docs/configuration.md +54 -0
- package/docs/container-lifecycle.md +349 -0
- package/docs/context-architecture.md +87 -0
- package/docs/deployment.md +199 -0
- package/docs/extensions.md +375 -0
- package/docs/graceful-shutdown.md +62 -0
- package/docs/kb-distillation.md +77 -0
- package/docs/media/overview.md +140 -0
- package/docs/media/whatsapp.md +171 -0
- package/docs/memory.md +137 -0
- package/docs/permissions.md +217 -0
- package/docs/pipeline.md +228 -0
- package/docs/prd-chat-memory.md +76 -0
- package/docs/prd-config-load.md +82 -0
- package/docs/rate-limiting.md +166 -0
- package/docs/scheduler.md +288 -0
- package/docs/setup-discord.md +100 -0
- package/docs/setup-slack.md +119 -0
- package/docs/setup-whatsapp.md +94 -0
- package/docs/subagents.md +166 -0
- package/docs/web-search.md +62 -0
- package/examples/extensions/README.md +12 -0
- package/examples/extensions/charts/index.ts +13 -0
- package/examples/extensions/charts/skill/SKILL.md +98 -0
- package/examples/extensions/gws/README.md +52 -0
- package/examples/extensions/gws/index.ts +106 -0
- package/examples/extensions/gws/skill/SKILL.md +57 -0
- package/examples/extensions/gws/skill/references/calendar.md +101 -0
- package/examples/extensions/gws/skill/references/docs.md +65 -0
- package/examples/extensions/gws/skill/references/drive.md +79 -0
- package/examples/extensions/gws/skill/references/gmail.md +85 -0
- package/examples/extensions/gws/skill/references/sheets.md +60 -0
- package/examples/extensions/napkin/index.ts +821 -0
- package/examples/extensions/napkin/prompts/consolidation-monthly.md +73 -0
- package/examples/extensions/napkin/prompts/consolidation-weekly.md +67 -0
- package/examples/extensions/napkin/prompts/kb-distillation.md +176 -0
- package/examples/extensions/napkin/skill/SKILL.md +728 -0
- package/examples/extensions/pdf/index.ts +23 -0
- package/examples/extensions/pdf/skill/LICENSE.txt +30 -0
- package/examples/extensions/pdf/skill/SKILL.md +314 -0
- package/examples/extensions/pdf/skill/forms.md +294 -0
- package/examples/extensions/pdf/skill/reference.md +612 -0
- package/examples/extensions/pdf/skill/scripts/check_bounding_boxes.py +65 -0
- package/examples/extensions/pdf/skill/scripts/check_fillable_fields.py +11 -0
- package/examples/extensions/pdf/skill/scripts/convert_pdf_to_images.py +33 -0
- package/examples/extensions/pdf/skill/scripts/create_validation_image.py +37 -0
- package/examples/extensions/pdf/skill/scripts/extract_form_field_info.py +122 -0
- package/examples/extensions/pdf/skill/scripts/extract_form_structure.py +115 -0
- package/examples/extensions/pdf/skill/scripts/fill_fillable_fields.py +98 -0
- package/examples/extensions/pdf/skill/scripts/fill_pdf_form_with_annotations.py +107 -0
- package/examples/extensions/permission-guard/index.ts +65 -0
- package/examples/extensions/pinchtab/index.ts +199 -0
- package/examples/extensions/pinchtab/lib/session-injector.ts +144 -0
- package/examples/extensions/pinchtab/skill/SKILL.md +224 -0
- package/examples/extensions/pinchtab/skill/TRUST.md +69 -0
- package/examples/extensions/pinchtab/skill/references/api.md +297 -0
- package/examples/extensions/pinchtab/skill/references/env.md +45 -0
- package/examples/extensions/pinchtab/skill/references/profiles.md +107 -0
- package/examples/extensions/tradestation/host/refresh.ts +102 -0
- package/examples/extensions/tradestation/index.ts +153 -0
- package/examples/extensions/tradestation/skill/SKILL.md +67 -0
- package/examples/extensions/tradestation/skill/scripts/ts-cli.ts +111 -0
- package/examples/extensions/voice-synth/index.ts +94 -0
- package/examples/extensions/voice-synth/skill/SKILL.md +38 -0
- package/examples/extensions/voice-transcribe/index.ts +381 -0
- package/examples/extensions/voice-transcribe/requirements.txt +8 -0
- package/examples/extensions/voice-transcribe/scripts/transcribe.py +179 -0
- package/examples/extensions/voice-transcribe/skill/SKILL.md +53 -0
- package/examples/extensions/web-search/index.ts +22 -0
- package/examples/extensions/web-search/skill/SKILL.md +114 -0
- package/examples/extensions/web-search/skill/references/apartments.md +178 -0
- package/examples/extensions/web-search/skill/references/car-purchase.md +132 -0
- package/examples/extensions/web-search/skill/references/car-rental.md +113 -0
- package/examples/extensions/web-search/skill/references/flights.md +133 -0
- package/examples/extensions/web-search/skill/references/hotels.md +148 -0
- package/examples/extensions/yahoo-mail/cli/bun.lock +66 -0
- package/examples/extensions/yahoo-mail/cli/package.json +13 -0
- package/examples/extensions/yahoo-mail/cli/ymail.mjs +353 -0
- package/examples/extensions/yahoo-mail/index.ts +57 -0
- package/examples/extensions/yahoo-mail/skill/SKILL.md +78 -0
- package/package.json +106 -0
- package/resources/agents/explore.md +50 -0
- package/resources/agents/worker.md +24 -0
- package/resources/builtin-extensions.txt +3 -0
- package/resources/connection-env-vars.json +25 -0
- package/resources/extensions/.gitkeep +0 -0
- package/resources/pi-extensions/subagent/agents.ts +126 -0
- package/resources/pi-extensions/subagent/index.ts +964 -0
- package/resources/profiles/coding/AGENTS.md +43 -0
- package/resources/profiles/coding/mercury-profile.yaml +15 -0
- package/resources/profiles/general/AGENTS.md +31 -0
- package/resources/profiles/general/mercury-profile.yaml +15 -0
- package/resources/profiles/research/AGENTS.md +40 -0
- package/resources/profiles/research/mercury-profile.yaml +15 -0
- package/resources/skills/config/SKILL.md +25 -0
- package/resources/skills/context/SKILL.md +33 -0
- package/resources/skills/conversation-recap/SKILL.md +19 -0
- package/resources/skills/media/SKILL.md +27 -0
- package/resources/skills/mutes/SKILL.md +31 -0
- package/resources/skills/permissions/SKILL.md +19 -0
- package/resources/skills/preferences/SKILL.md +31 -0
- package/resources/skills/recall/SKILL.md +24 -0
- package/resources/skills/roles/SKILL.md +18 -0
- package/resources/skills/spaces/SKILL.md +18 -0
- package/resources/skills/tasks/SKILL.md +45 -0
- package/resources/templates/AGENTS.md +157 -0
- package/resources/templates/env.template +34 -0
- package/resources/templates/mercury.example.yaml +75 -0
- package/src/adapters/discord-native.ts +534 -0
- package/src/adapters/discord.ts +38 -0
- package/src/adapters/setup.ts +89 -0
- package/src/adapters/slack.ts +9 -0
- package/src/adapters/whatsapp-media.ts +337 -0
- package/src/adapters/whatsapp.ts +629 -0
- package/src/agent/api-socket.ts +127 -0
- package/src/agent/container-entry.ts +967 -0
- package/src/agent/container-error.ts +49 -0
- package/src/agent/container-runner.ts +1272 -0
- package/src/agent/model-capabilities-core.ts +23 -0
- package/src/agent/model-capabilities.ts +231 -0
- package/src/agent/pi-failure-class.ts +83 -0
- package/src/agent/pi-jsonl-parser.ts +306 -0
- package/src/agent/preferences-prompt.ts +20 -0
- package/src/agent/user-error-messages.ts +78 -0
- package/src/bridges/discord.ts +171 -0
- package/src/bridges/slack.ts +177 -0
- package/src/bridges/teams.ts +160 -0
- package/src/bridges/telegram.ts +571 -0
- package/src/bridges/whatsapp.ts +290 -0
- package/src/chat-shim.ts +259 -0
- package/src/cli/mercury.ts +2508 -0
- package/src/cli/mrctl-http.ts +27 -0
- package/src/cli/mrctl.ts +611 -0
- package/src/cli/whatsapp-auth.ts +260 -0
- package/src/config-file.ts +397 -0
- package/src/config-model-chain.ts +30 -0
- package/src/config.ts +316 -0
- package/src/core/api-types.ts +58 -0
- package/src/core/api.ts +105 -0
- package/src/core/commands.ts +76 -0
- package/src/core/conversation.ts +47 -0
- package/src/core/handler.ts +206 -0
- package/src/core/media.ts +200 -0
- package/src/core/mute-duration.ts +22 -0
- package/src/core/outbox.ts +76 -0
- package/src/core/permissions.ts +192 -0
- package/src/core/profiles.ts +245 -0
- package/src/core/rate-limiter.ts +127 -0
- package/src/core/router.ts +191 -0
- package/src/core/routes/chat.ts +172 -0
- package/src/core/routes/config-builtin.ts +107 -0
- package/src/core/routes/config.ts +81 -0
- package/src/core/routes/connections.ts +190 -0
- package/src/core/routes/console.ts +668 -0
- package/src/core/routes/control.ts +46 -0
- package/src/core/routes/conversations.ts +66 -0
- package/src/core/routes/dashboard.ts +2491 -0
- package/src/core/routes/extensions.ts +37 -0
- package/src/core/routes/index.ts +14 -0
- package/src/core/routes/media.ts +72 -0
- package/src/core/routes/messages.ts +37 -0
- package/src/core/routes/mutes.ts +89 -0
- package/src/core/routes/prefs.ts +95 -0
- package/src/core/routes/roles.ts +125 -0
- package/src/core/routes/spaces.ts +60 -0
- package/src/core/routes/storage.ts +126 -0
- package/src/core/routes/tasks.ts +189 -0
- package/src/core/routes/tradestation.ts +268 -0
- package/src/core/routes/tts.ts +51 -0
- package/src/core/runtime.ts +1140 -0
- package/src/core/space-queue.ts +103 -0
- package/src/core/storage-cleanup.ts +140 -0
- package/src/core/storage-guard.ts +24 -0
- package/src/core/task-scheduler.ts +132 -0
- package/src/core/telegram-format.ts +178 -0
- package/src/core/trigger.ts +142 -0
- package/src/dashboard/index.html +729 -0
- package/src/dashboard/tokens.css +53 -0
- package/src/extensions/api.ts +252 -0
- package/src/extensions/catalog.ts +117 -0
- package/src/extensions/config-registry.ts +83 -0
- package/src/extensions/context.ts +36 -0
- package/src/extensions/hooks.ts +156 -0
- package/src/extensions/image-builder.ts +617 -0
- package/src/extensions/installer.ts +306 -0
- package/src/extensions/jobs.ts +122 -0
- package/src/extensions/loader.ts +271 -0
- package/src/extensions/permission-guard.ts +52 -0
- package/src/extensions/reserved.ts +28 -0
- package/src/extensions/skills.ts +123 -0
- package/src/extensions/types.ts +462 -0
- package/src/logger.ts +174 -0
- package/src/main.ts +586 -0
- package/src/server.ts +391 -0
- package/src/storage/db.ts +1624 -0
- package/src/storage/memory.ts +45 -0
- package/src/storage/pi-auth.ts +95 -0
- package/src/text/markdown.ts +117 -0
- package/src/text/rtl.ts +38 -0
- package/src/tradestation/host-api.ts +77 -0
- package/src/tradestation/pending-orders.ts +69 -0
- package/src/tts/azure.ts +52 -0
- package/src/tts/google.ts +128 -0
- package/src/tts/index.ts +8 -0
- package/src/tts/language.ts +20 -0
- package/src/tts/synthesize.ts +133 -0
- package/src/types.ts +295 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolveProjectPath } from "../config.js";
|
|
3
|
+
import { synthesizeAzure } from "./azure.js";
|
|
4
|
+
import { synthesizeGoogle } from "./google.js";
|
|
5
|
+
import {
|
|
6
|
+
resolveTtsLanguageFromText,
|
|
7
|
+
type TtsLanguageInput,
|
|
8
|
+
} from "./language.js";
|
|
9
|
+
|
|
10
|
+
export class TtsConfigError extends Error {
|
|
11
|
+
readonly code: "not_configured";
|
|
12
|
+
|
|
13
|
+
constructor(message: string) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "TtsConfigError";
|
|
16
|
+
this.code = "not_configured";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Subset of AppConfig used by TTS (extensions import via `mercury-agent/tts`). */
|
|
21
|
+
export interface MercuryTtsConfig {
|
|
22
|
+
ttsProvider: "google" | "azure" | "auto";
|
|
23
|
+
azureSpeechKey?: string;
|
|
24
|
+
azureSpeechRegion?: string;
|
|
25
|
+
googleApplicationCredentials?: string;
|
|
26
|
+
ttsMaxChars: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TtsSynthesizeOptions {
|
|
30
|
+
text: string;
|
|
31
|
+
language?: TtsLanguageInput;
|
|
32
|
+
providerOverride?: "google" | "azure" | "auto";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface TtsSynthesizeResult {
|
|
36
|
+
buffer: Buffer;
|
|
37
|
+
mimeType: string;
|
|
38
|
+
filename: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function hasGoogleCredentials(config: MercuryTtsConfig): boolean {
|
|
42
|
+
const p = config.googleApplicationCredentials?.trim();
|
|
43
|
+
if (!p) return false;
|
|
44
|
+
try {
|
|
45
|
+
return existsSync(resolveProjectPath(p));
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function hasAzure(config: MercuryTtsConfig): boolean {
|
|
52
|
+
return Boolean(
|
|
53
|
+
config.azureSpeechKey?.trim() && config.azureSpeechRegion?.trim(),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveEffectiveBackend(
|
|
58
|
+
config: MercuryTtsConfig,
|
|
59
|
+
override?: "google" | "azure" | "auto",
|
|
60
|
+
): "google" | "azure" {
|
|
61
|
+
const mode = override ?? config.ttsProvider;
|
|
62
|
+
|
|
63
|
+
if (mode === "google") {
|
|
64
|
+
if (!hasGoogleCredentials(config)) {
|
|
65
|
+
throw new TtsConfigError(
|
|
66
|
+
"Google TTS selected but MERCURY_GOOGLE_APPLICATION_CREDENTIALS / GOOGLE_APPLICATION_CREDENTIALS file is missing or not found",
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return "google";
|
|
70
|
+
}
|
|
71
|
+
if (mode === "azure") {
|
|
72
|
+
if (!hasAzure(config)) {
|
|
73
|
+
throw new TtsConfigError(
|
|
74
|
+
"Azure TTS selected but MERCURY_AZURE_SPEECH_KEY or MERCURY_AZURE_SPEECH_REGION is missing",
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return "azure";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (hasGoogleCredentials(config)) return "google";
|
|
81
|
+
if (hasAzure(config)) return "azure";
|
|
82
|
+
|
|
83
|
+
throw new TtsConfigError(
|
|
84
|
+
"No TTS provider configured: set Google service account path or Azure key+region",
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Synthesize speech using host credentials. Intended for `/api/tts` and voice-synth extension.
|
|
90
|
+
*/
|
|
91
|
+
export async function synthesizeSpeech(
|
|
92
|
+
config: MercuryTtsConfig,
|
|
93
|
+
opts: TtsSynthesizeOptions,
|
|
94
|
+
): Promise<TtsSynthesizeResult> {
|
|
95
|
+
const trimmed = opts.text.trim();
|
|
96
|
+
if (!trimmed) {
|
|
97
|
+
throw new Error("TTS text is empty");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const max = Math.min(10_000, Math.max(500, config.ttsMaxChars));
|
|
101
|
+
if (trimmed.length > max) {
|
|
102
|
+
throw new Error(`TTS text exceeds max length (${max} characters)`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const language = resolveTtsLanguageFromText(trimmed, opts.language);
|
|
106
|
+
const backend = resolveEffectiveBackend(config, opts.providerOverride);
|
|
107
|
+
|
|
108
|
+
let buffer: Buffer;
|
|
109
|
+
if (backend === "azure") {
|
|
110
|
+
buffer = await synthesizeAzure({
|
|
111
|
+
key: config.azureSpeechKey as string,
|
|
112
|
+
region: config.azureSpeechRegion as string,
|
|
113
|
+
text: trimmed,
|
|
114
|
+
language,
|
|
115
|
+
});
|
|
116
|
+
} else {
|
|
117
|
+
const credPath = resolveProjectPath(
|
|
118
|
+
config.googleApplicationCredentials?.trim() ?? "",
|
|
119
|
+
);
|
|
120
|
+
buffer = await synthesizeGoogle({
|
|
121
|
+
credentialsPath: credPath,
|
|
122
|
+
text: trimmed,
|
|
123
|
+
language,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
buffer,
|
|
129
|
+
mimeType: "audio/mpeg",
|
|
130
|
+
filename:
|
|
131
|
+
language === "he-IL" ? "mercury-tts-he.mp3" : "mercury-tts-en.mp3",
|
|
132
|
+
};
|
|
133
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
export type MessageRole = "user" | "assistant" | "tool" | "ambient";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Media type classification.
|
|
5
|
+
* Generic across platforms — each adapter maps its native types to these.
|
|
6
|
+
*/
|
|
7
|
+
export type MediaType = "image" | "video" | "audio" | "voice" | "document";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Attachment metadata for messages.
|
|
11
|
+
* Platform-agnostic — adapters populate these fields from platform-specific data.
|
|
12
|
+
*/
|
|
13
|
+
export interface MessageAttachment {
|
|
14
|
+
/** Local file path (relative to group workspace) */
|
|
15
|
+
path: string;
|
|
16
|
+
/** Media type classification */
|
|
17
|
+
type: MediaType;
|
|
18
|
+
/** MIME type (e.g., "image/jpeg", "audio/ogg") */
|
|
19
|
+
mimeType: string;
|
|
20
|
+
/** Original filename if available */
|
|
21
|
+
filename?: string;
|
|
22
|
+
/** File size in bytes */
|
|
23
|
+
sizeBytes?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface MessageAgentRunMeta {
|
|
27
|
+
inputTokens?: number;
|
|
28
|
+
outputTokens?: number;
|
|
29
|
+
totalTokens?: number;
|
|
30
|
+
cacheReadTokens?: number;
|
|
31
|
+
cacheWriteTokens?: number;
|
|
32
|
+
cost?: number;
|
|
33
|
+
model?: string;
|
|
34
|
+
provider?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface MessageRunMeta {
|
|
38
|
+
agent?: MessageAgentRunMeta;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface StoredMessage {
|
|
42
|
+
id: number;
|
|
43
|
+
spaceId: string;
|
|
44
|
+
role: MessageRole;
|
|
45
|
+
content: string;
|
|
46
|
+
/** Attachments (images, voice notes, documents, etc.) */
|
|
47
|
+
attachments?: MessageAttachment[];
|
|
48
|
+
createdAt: number;
|
|
49
|
+
updatedAt: number;
|
|
50
|
+
/** Set after the agent turn completes for `user` rows */
|
|
51
|
+
runMeta?: MessageRunMeta;
|
|
52
|
+
/** Mercury message ID this message is a reply to (for reply chain assembly) */
|
|
53
|
+
replyToId?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ScheduledTask {
|
|
57
|
+
id: number;
|
|
58
|
+
spaceId: string;
|
|
59
|
+
cron: string | null; // null for at-tasks
|
|
60
|
+
at: string | null; // ISO 8601 timestamp, null for cron-tasks
|
|
61
|
+
prompt: string;
|
|
62
|
+
active: number;
|
|
63
|
+
silent: number;
|
|
64
|
+
nextRunAt: number;
|
|
65
|
+
createdBy: string;
|
|
66
|
+
timezone: string | null;
|
|
67
|
+
name: string | null;
|
|
68
|
+
createdAt: number;
|
|
69
|
+
updatedAt: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface Space {
|
|
73
|
+
/** User-chosen slug, primary key. e.g. "family", "work" */
|
|
74
|
+
id: string;
|
|
75
|
+
/** Human display name. e.g. "Family Ops" */
|
|
76
|
+
name: string;
|
|
77
|
+
/** Comma-separated tags for metadata grouping */
|
|
78
|
+
tags: string | null;
|
|
79
|
+
createdAt: number;
|
|
80
|
+
updatedAt: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface Conversation {
|
|
84
|
+
id: number;
|
|
85
|
+
/** "whatsapp" | "discord" | "slack" | "telegram" | "api" */
|
|
86
|
+
platform: string;
|
|
87
|
+
/** Platform-native ID (WhatsApp JID, Discord channel ID, etc.) */
|
|
88
|
+
externalId: string;
|
|
89
|
+
/** "dm" | "group" | "channel" | "thread" */
|
|
90
|
+
kind: string;
|
|
91
|
+
/** Title observed from platform, if available */
|
|
92
|
+
observedTitle: string | null;
|
|
93
|
+
/** Linked space slug, or null if unlinked */
|
|
94
|
+
spaceId: string | null;
|
|
95
|
+
firstSeenAt: number;
|
|
96
|
+
lastSeenAt: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface SpaceRole {
|
|
100
|
+
spaceId: string;
|
|
101
|
+
platformUserId: string;
|
|
102
|
+
displayName: string | null;
|
|
103
|
+
role: string;
|
|
104
|
+
grantedBy: string | null;
|
|
105
|
+
createdAt: number;
|
|
106
|
+
updatedAt: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface SpaceConfigEntry {
|
|
110
|
+
spaceId: string;
|
|
111
|
+
key: string;
|
|
112
|
+
value: string;
|
|
113
|
+
updatedBy: string | null;
|
|
114
|
+
createdAt: number;
|
|
115
|
+
updatedAt: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Per-space preference row (chat-managed hints for the assistant). */
|
|
119
|
+
export interface SpacePreferenceEntry {
|
|
120
|
+
spaceId: string;
|
|
121
|
+
key: string;
|
|
122
|
+
value: string;
|
|
123
|
+
createdBy: string;
|
|
124
|
+
createdAt: number;
|
|
125
|
+
updatedAt: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export type TriggerMatch = "prefix" | "mention" | "always";
|
|
129
|
+
|
|
130
|
+
export interface TriggerConfig {
|
|
131
|
+
match: TriggerMatch;
|
|
132
|
+
patterns: string[]; // e.g. ["@Mick", "Mick"]
|
|
133
|
+
caseSensitive: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* When true, attachment-only messages (e.g. voice) trigger the assistant in
|
|
136
|
+
* group chats without a text prefix. Default false (DMs always allow this).
|
|
137
|
+
*/
|
|
138
|
+
mediaInGroups: boolean;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Abstraction for sending messages to groups.
|
|
143
|
+
* Implemented by chat-sdk, used by runtime for scheduled task replies.
|
|
144
|
+
*/
|
|
145
|
+
export interface MessageSender {
|
|
146
|
+
/**
|
|
147
|
+
* Send a text message (and optional file attachments) to a group.
|
|
148
|
+
* @param groupId - The group identifier (encodes platform, e.g., "whatsapp:123@lid:456@lid")
|
|
149
|
+
* @param text - The message text to send
|
|
150
|
+
* @param files - Optional file attachments to send with the message
|
|
151
|
+
*/
|
|
152
|
+
send(groupId: string, text: string, files?: EgressFile[]): Promise<void>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ─── Ingress / Egress Pipeline Types ────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Normalized inbound message — every adapter produces this.
|
|
159
|
+
* All fields are required (no optional booleans or arrays) to enforce
|
|
160
|
+
* that adapters make explicit decisions about every field.
|
|
161
|
+
*/
|
|
162
|
+
export interface IngressMessage {
|
|
163
|
+
/** Platform identifier: "whatsapp" | "slack" | "discord" | "telegram" */
|
|
164
|
+
platform: string;
|
|
165
|
+
/** Resolved space ID */
|
|
166
|
+
spaceId: string;
|
|
167
|
+
/** Platform-native conversation ID (NOT the space ID) */
|
|
168
|
+
conversationExternalId: string;
|
|
169
|
+
/** Platform-qualified caller: "whatsapp:jid", "discord:123", "slack:U123" */
|
|
170
|
+
callerId: string;
|
|
171
|
+
/** Display name of the message author */
|
|
172
|
+
authorName?: string;
|
|
173
|
+
/** Message text with mentions normalized */
|
|
174
|
+
text: string;
|
|
175
|
+
/** Whether this is a direct message (1:1) */
|
|
176
|
+
isDM: boolean;
|
|
177
|
+
/** Whether the message is a reply to the bot */
|
|
178
|
+
isReplyToBot: boolean;
|
|
179
|
+
/** Already-downloaded attachments (local paths) */
|
|
180
|
+
attachments: MessageAttachment[];
|
|
181
|
+
/**
|
|
182
|
+
* True if the platform message carried attachments or downloadable media
|
|
183
|
+
* before normalize (even if nothing was saved — e.g. media off or download failed).
|
|
184
|
+
*/
|
|
185
|
+
hadIncomingAttachments?: boolean;
|
|
186
|
+
/** Platform-native message ID of the message being replied to (if this is a reply) */
|
|
187
|
+
replyToPlatformMessageId?: string;
|
|
188
|
+
/** Platform-native message ID of THIS inbound message (for future reply tracking) */
|
|
189
|
+
platformMessageId?: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* File the model wants to send back (scanned from workspace outbox/ directory).
|
|
194
|
+
*/
|
|
195
|
+
export interface EgressFile {
|
|
196
|
+
/** Absolute local file path */
|
|
197
|
+
path: string;
|
|
198
|
+
/** Display name for the recipient */
|
|
199
|
+
filename: string;
|
|
200
|
+
/** MIME type (detected from extension) */
|
|
201
|
+
mimeType: string;
|
|
202
|
+
/** File size in bytes */
|
|
203
|
+
sizeBytes: number;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Token usage reported by the model provider via pi's JSON output.
|
|
208
|
+
*/
|
|
209
|
+
export interface TokenUsage {
|
|
210
|
+
inputTokens?: number;
|
|
211
|
+
outputTokens?: number;
|
|
212
|
+
totalTokens?: number;
|
|
213
|
+
cacheReadTokens?: number;
|
|
214
|
+
cacheWriteTokens?: number;
|
|
215
|
+
cost?: number;
|
|
216
|
+
model?: string;
|
|
217
|
+
provider?: string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* What a container run produces.
|
|
222
|
+
* Replaces the plain string return from containerRunner.reply().
|
|
223
|
+
*/
|
|
224
|
+
export interface ContainerResult {
|
|
225
|
+
/** Text reply from the model */
|
|
226
|
+
reply: string;
|
|
227
|
+
/** Files scanned from workspace outbox/ (new or modified during the run) */
|
|
228
|
+
files: EgressFile[];
|
|
229
|
+
/** Token usage from the model provider, if available */
|
|
230
|
+
usage?: TokenUsage;
|
|
231
|
+
/** Mercury message ID of the stored assistant reply (set by runtime, used for platform ID mapping) */
|
|
232
|
+
assistantMessageId?: number;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Context passed to PlatformBridge.normalize() for media download decisions.
|
|
237
|
+
*/
|
|
238
|
+
export interface NormalizeContext {
|
|
239
|
+
/** Bot's display name (for mention normalization) */
|
|
240
|
+
botUserName: string;
|
|
241
|
+
/** Get workspace path for a space (for media download destination) */
|
|
242
|
+
getWorkspace: (spaceId: string) => string;
|
|
243
|
+
/** Media download settings */
|
|
244
|
+
media: { enabled: boolean; maxSizeBytes: number };
|
|
245
|
+
/** Returns true when the agent's disk quota is exceeded. Skips media download when true. */
|
|
246
|
+
isOverQuota: () => Promise<boolean>;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Complete platform integration contract.
|
|
251
|
+
*
|
|
252
|
+
* Each platform (WhatsApp, Discord, Slack, Telegram) implements this interface to cover
|
|
253
|
+
* both inbound normalization and outbound reply+file sending.
|
|
254
|
+
*/
|
|
255
|
+
export interface PlatformBridge {
|
|
256
|
+
readonly platform: string;
|
|
257
|
+
|
|
258
|
+
// --- Pure helpers (used before full normalize, e.g. for typing indicator) ---
|
|
259
|
+
|
|
260
|
+
/** Extract conversation identity and DM state from a thread ID */
|
|
261
|
+
parseThread(threadId: string): { externalId: string; isDM: boolean };
|
|
262
|
+
|
|
263
|
+
// --- Ingress ---
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Normalize a raw platform message into IngressMessage.
|
|
267
|
+
* Handles mention replacement, reply-to-bot detection, media download.
|
|
268
|
+
* Returns null to skip the message (e.g., bot's own messages, empty).
|
|
269
|
+
*/
|
|
270
|
+
normalize(
|
|
271
|
+
threadId: string,
|
|
272
|
+
message: unknown,
|
|
273
|
+
ctx: NormalizeContext,
|
|
274
|
+
spaceId: string,
|
|
275
|
+
): Promise<IngressMessage | null>;
|
|
276
|
+
|
|
277
|
+
// --- Egress ---
|
|
278
|
+
|
|
279
|
+
/** Send a reply with optional file attachments. Returns the platform message ID if available. */
|
|
280
|
+
sendReply(
|
|
281
|
+
threadId: string,
|
|
282
|
+
text: string,
|
|
283
|
+
files?: EgressFile[],
|
|
284
|
+
): Promise<string | undefined>;
|
|
285
|
+
|
|
286
|
+
/** Delete previously sent messages by ID. Best-effort — failures must be swallowed. */
|
|
287
|
+
deleteMessages?(threadId: string, messageIds: string[]): Promise<void>;
|
|
288
|
+
|
|
289
|
+
/** Edit a previously sent message in-place. Returns true if the edit succeeded. */
|
|
290
|
+
editMessage?(
|
|
291
|
+
threadId: string,
|
|
292
|
+
messageId: string,
|
|
293
|
+
text: string,
|
|
294
|
+
): Promise<boolean>;
|
|
295
|
+
}
|