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,127 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Per-agent inner→outer API unix socket.
|
|
7
|
+
*
|
|
8
|
+
* In gVisor (runsc) mode the outer agent container leaves the shared Docker
|
|
9
|
+
* default bridge (docker0), so inner containers can no longer reach the API over
|
|
10
|
+
* TCP. They reach it over a unix socket that lives in the per-agent data volume
|
|
11
|
+
* — already visible to host-sibling inner containers through the
|
|
12
|
+
* MERCURY_HOST_DATA_DIR host-path translation.
|
|
13
|
+
*
|
|
14
|
+
* The socket name is per-container-unique (`api-<hostname>.sock`) because during
|
|
15
|
+
* a blue-green deploy the canonical container and its `-next` sibling mount the
|
|
16
|
+
* *same* data volume simultaneously; a fixed name would collide on the second
|
|
17
|
+
* bind. `<hostname>` is the Docker short container ID (the container's hostname),
|
|
18
|
+
* so each outer process owns a distinct socket and injects its own name into the
|
|
19
|
+
* inner containers it spawns.
|
|
20
|
+
*
|
|
21
|
+
* Requires the Docker daemon to register `runsc` with `--host-uds=open` so the
|
|
22
|
+
* gVisor gofer will proxy `connect()` to the bind-mounted host socket — see
|
|
23
|
+
* node-cloud-init.ts.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** Directory (relative to the agent data dir) holding per-container API sockets. */
|
|
27
|
+
export const API_SOCKET_SUBDIR = "run";
|
|
28
|
+
|
|
29
|
+
/** Mount point for the run dir inside inner gVisor containers. */
|
|
30
|
+
export const INNER_RUN_DIR = "/run/mercury";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Sanitize a hostname into a filesystem-safe socket-name component. Docker sets
|
|
34
|
+
* the container hostname to the short container ID (hex), but we defensively
|
|
35
|
+
* strip anything outside `[a-zA-Z0-9_-]` in case of a custom `--hostname`.
|
|
36
|
+
*/
|
|
37
|
+
function sanitizeHostname(hostname: string): string {
|
|
38
|
+
const cleaned = hostname.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
39
|
+
return cleaned || "default";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Per-container socket file name, e.g. `api-3f9c2a1b4d5e.sock`. */
|
|
43
|
+
export function apiSocketName(hostname: string = os.hostname()): string {
|
|
44
|
+
return `api-${sanitizeHostname(hostname)}.sock`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Run dir holding the API sockets, given a (resolved) data-dir base path. */
|
|
48
|
+
export function apiSocketDir(dataDir: string): string {
|
|
49
|
+
return path.join(dataDir, API_SOCKET_SUBDIR);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Absolute path to this container's API socket inside the outer container. */
|
|
53
|
+
export function apiSocketPath(
|
|
54
|
+
dataDir: string,
|
|
55
|
+
hostname: string = os.hostname(),
|
|
56
|
+
): string {
|
|
57
|
+
return path.join(apiSocketDir(dataDir), apiSocketName(hostname));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Path an inner container uses to reach the socket (the bind-mounted run dir).
|
|
62
|
+
* Always POSIX — this is a Linux-container path regardless of the host OS.
|
|
63
|
+
*/
|
|
64
|
+
export function innerApiSocketPath(hostname: string = os.hostname()): string {
|
|
65
|
+
return path.posix.join(INNER_RUN_DIR, apiSocketName(hostname));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Connect-test a socket: any HTTP response (even 503 "warming") means a server
|
|
70
|
+
* is listening. A refused/timed-out connect means the socket is an orphan inode.
|
|
71
|
+
*/
|
|
72
|
+
export async function isApiSocketLive(socketPath: string): Promise<boolean> {
|
|
73
|
+
try {
|
|
74
|
+
const res = await fetch("http://localhost/health", {
|
|
75
|
+
unix: socketPath,
|
|
76
|
+
signal: AbortSignal.timeout(500),
|
|
77
|
+
} as RequestInit & { unix: string });
|
|
78
|
+
await res.body?.cancel();
|
|
79
|
+
return true;
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Sweep orphan API sockets left on the persistent data volume by prior deploys.
|
|
87
|
+
*
|
|
88
|
+
* Each blue-green `-next` binds a fresh `api-<hostname>.sock`; the removed old
|
|
89
|
+
* container leaves its 0-byte socket inode behind. On startup we connect-test
|
|
90
|
+
* every `api-*.sock` except our own and unlink the dead ones. Safe during a
|
|
91
|
+
* blue-green swap: a live sibling's socket answers the connect and is kept; a
|
|
92
|
+
* sibling that is mid-startup (socket file present but not yet listening) may be
|
|
93
|
+
* unlinked, but it re-creates the file on bind (unlink-stale-then-listen), so no
|
|
94
|
+
* correctness issue. Orphans are inert if left — this is hygiene only.
|
|
95
|
+
*/
|
|
96
|
+
export async function sweepOrphanApiSockets(
|
|
97
|
+
dataDir: string,
|
|
98
|
+
keepName: string,
|
|
99
|
+
log?: {
|
|
100
|
+
info: (msg: string, meta?: Record<string, unknown>) => void;
|
|
101
|
+
warn: (msg: string, meta?: Record<string, unknown>) => void;
|
|
102
|
+
},
|
|
103
|
+
isLive: (socketPath: string) => Promise<boolean> = isApiSocketLive,
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
const dir = apiSocketDir(dataDir);
|
|
106
|
+
let entries: string[];
|
|
107
|
+
try {
|
|
108
|
+
entries = fs.readdirSync(dir);
|
|
109
|
+
} catch {
|
|
110
|
+
return; // run dir doesn't exist yet — nothing to sweep
|
|
111
|
+
}
|
|
112
|
+
for (const name of entries) {
|
|
113
|
+
if (!name.startsWith("api-") || !name.endsWith(".sock")) continue;
|
|
114
|
+
if (name === keepName) continue;
|
|
115
|
+
const full = path.join(dir, name);
|
|
116
|
+
if (await isLive(full)) continue; // live sibling — keep
|
|
117
|
+
try {
|
|
118
|
+
fs.unlinkSync(full);
|
|
119
|
+
log?.info("Swept orphan API socket", { socket: full });
|
|
120
|
+
} catch (err) {
|
|
121
|
+
log?.warn("Failed to unlink orphan API socket", {
|
|
122
|
+
socket: full,
|
|
123
|
+
error: err instanceof Error ? err.message : String(err),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|