mercury-agent 0.4.28 → 0.5.1

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.
Files changed (122) hide show
  1. package/README.md +451 -451
  2. package/container/Dockerfile +127 -127
  3. package/container/Dockerfile.base +109 -109
  4. package/container/Dockerfile.power +17 -17
  5. package/container/agent-package.json +8 -8
  6. package/container/build.sh +54 -54
  7. package/docs/ARCHITECTURE.md +4 -0
  8. package/docs/ROADMAP.md +4 -5
  9. package/docs/{backlog/applicative-profiles.md → archive/applicative-profiles/2026-07-01-applicative-profiles.md} +29 -18
  10. package/docs/{backlog/caller-bound-capability-token.md → archive/applicative-profiles/2026-07-01-caller-bound-capability-token.md} +15 -11
  11. package/docs/archive/summarization/2026-07-02-recent-dev-summary.md +71 -0
  12. package/docs/auth/dashboard.md +28 -28
  13. package/docs/auth/overview.md +109 -109
  14. package/docs/auth/whatsapp.md +173 -173
  15. package/docs/authoring-profiles.md +174 -0
  16. package/docs/configuration.md +54 -54
  17. package/docs/container-lifecycle.md +349 -349
  18. package/docs/{bugs/bwrap-privileged-linux-docker.md → debug/major/2026-07-02-bwrap-privileged-linux-docker.md} +21 -1
  19. package/docs/{bugs/e2big-prompt-delivery-fix.md → debug/major/2026-07-02-e2big-prompt-delivery-fix.md} +27 -1
  20. package/docs/{bugs/registry-pull-no-local-fallback.md → debug/major/2026-07-02-registry-pull-no-local-fallback.md} +27 -1
  21. package/docs/debug/summarization/2026-07-02-common-bug-patterns.md +72 -0
  22. package/docs/deployment.md +199 -199
  23. package/docs/extensions.md +375 -375
  24. package/docs/graceful-shutdown.md +62 -62
  25. package/docs/kb-distillation.md +77 -77
  26. package/docs/media/overview.md +140 -140
  27. package/docs/media/whatsapp.md +171 -171
  28. package/docs/memory.md +137 -137
  29. package/docs/permissions.md +217 -217
  30. package/docs/pipeline.md +228 -228
  31. package/docs/prd-chat-memory.md +76 -76
  32. package/docs/prd-config-load.md +82 -82
  33. package/docs/rate-limiting.md +229 -229
  34. package/docs/scheduler.md +288 -288
  35. package/docs/setup-discord.md +100 -100
  36. package/docs/setup-slack.md +119 -119
  37. package/docs/setup-whatsapp.md +94 -94
  38. package/docs/subagents.md +166 -166
  39. package/docs/web-search.md +62 -62
  40. package/examples/extensions/README.md +12 -12
  41. package/examples/extensions/charts/index.ts +13 -13
  42. package/examples/extensions/charts/skill/SKILL.md +98 -98
  43. package/examples/extensions/gws/README.md +52 -52
  44. package/examples/extensions/gws/skill/SKILL.md +57 -57
  45. package/examples/extensions/gws/skill/references/calendar.md +101 -101
  46. package/examples/extensions/gws/skill/references/docs.md +65 -65
  47. package/examples/extensions/gws/skill/references/drive.md +79 -79
  48. package/examples/extensions/gws/skill/references/gmail.md +85 -85
  49. package/examples/extensions/gws/skill/references/sheets.md +60 -60
  50. package/examples/extensions/napkin/skill/SKILL.md +728 -728
  51. package/examples/extensions/pdf/skill/LICENSE.txt +30 -30
  52. package/examples/extensions/pdf/skill/SKILL.md +314 -314
  53. package/examples/extensions/pdf/skill/forms.md +294 -294
  54. package/examples/extensions/pdf/skill/reference.md +611 -611
  55. package/examples/extensions/pdf/skill/scripts/check_bounding_boxes.py +65 -65
  56. package/examples/extensions/pdf/skill/scripts/check_fillable_fields.py +11 -11
  57. package/examples/extensions/pdf/skill/scripts/convert_pdf_to_images.py +33 -33
  58. package/examples/extensions/pdf/skill/scripts/create_validation_image.py +37 -37
  59. package/examples/extensions/pdf/skill/scripts/extract_form_field_info.py +122 -122
  60. package/examples/extensions/pdf/skill/scripts/extract_form_structure.py +115 -115
  61. package/examples/extensions/pdf/skill/scripts/fill_fillable_fields.py +98 -98
  62. package/examples/extensions/pdf/skill/scripts/fill_pdf_form_with_annotations.py +107 -107
  63. package/examples/extensions/permission-guard/index.ts +65 -65
  64. package/examples/extensions/pinchtab/skill/SKILL.md +224 -224
  65. package/examples/extensions/pinchtab/skill/TRUST.md +69 -69
  66. package/examples/extensions/pinchtab/skill/references/api.md +297 -297
  67. package/examples/extensions/pinchtab/skill/references/env.md +45 -45
  68. package/examples/extensions/pinchtab/skill/references/profiles.md +107 -107
  69. package/examples/extensions/tradestation/host/refresh.ts +102 -102
  70. package/examples/extensions/tradestation/index.ts +153 -153
  71. package/examples/extensions/tradestation/skill/SKILL.md +67 -67
  72. package/examples/extensions/voice-synth/index.ts +94 -94
  73. package/examples/extensions/voice-synth/skill/SKILL.md +38 -38
  74. package/examples/extensions/voice-transcribe/requirements.txt +8 -8
  75. package/examples/extensions/voice-transcribe/scripts/transcribe.py +179 -179
  76. package/examples/extensions/voice-transcribe/skill/SKILL.md +53 -53
  77. package/examples/extensions/yahoo-mail/cli/package.json +13 -13
  78. package/examples/extensions/yahoo-mail/skill/SKILL.md +78 -78
  79. package/package.json +106 -106
  80. package/resources/agents/explore.md +50 -50
  81. package/resources/agents/worker.md +24 -24
  82. package/resources/connection-env-vars.json +25 -25
  83. package/resources/pi-extensions/subagent/agents.ts +126 -126
  84. package/resources/pi-extensions/subagent/index.ts +964 -964
  85. package/resources/profiles/coding/AGENTS.md +43 -43
  86. package/resources/profiles/coding/mercury-profile.yaml +15 -15
  87. package/resources/profiles/general/AGENTS.md +31 -31
  88. package/resources/profiles/general/mercury-profile.yaml +15 -15
  89. package/resources/profiles/research/AGENTS.md +40 -40
  90. package/resources/profiles/research/mercury-profile.yaml +15 -15
  91. package/resources/skills/config/SKILL.md +25 -25
  92. package/resources/skills/context/SKILL.md +33 -33
  93. package/resources/skills/conversation-recap/SKILL.md +19 -19
  94. package/resources/skills/mutes/SKILL.md +31 -31
  95. package/resources/skills/permissions/SKILL.md +19 -19
  96. package/resources/skills/preferences/SKILL.md +31 -31
  97. package/resources/skills/recall/SKILL.md +24 -24
  98. package/resources/skills/roles/SKILL.md +18 -18
  99. package/resources/skills/spaces/SKILL.md +18 -18
  100. package/resources/skills/tasks/SKILL.md +45 -45
  101. package/resources/templates/AGENTS.md +157 -157
  102. package/resources/templates/env.template +38 -38
  103. package/resources/templates/mercury.example.yaml +15 -0
  104. package/src/agent/container-runner.ts +23 -0
  105. package/src/cli/mercury.ts +38 -6
  106. package/src/cli/mrctl.ts +25 -0
  107. package/src/config-file.ts +3 -0
  108. package/src/config.ts +15 -0
  109. package/src/core/api.ts +23 -3
  110. package/src/core/caller-token.ts +101 -0
  111. package/src/core/permissions.ts +37 -5
  112. package/src/core/profiles.ts +110 -84
  113. package/src/core/routes/capability.ts +70 -0
  114. package/src/core/routes/index.ts +1 -0
  115. package/src/core/runtime.ts +14 -0
  116. package/src/dashboard/index.html +729 -729
  117. package/src/extensions/api.ts +21 -0
  118. package/src/extensions/loader.ts +15 -0
  119. package/src/extensions/types.ts +55 -0
  120. package/src/main.ts +19 -0
  121. package/docs/pending-updates/dm-auto-space.md +0 -15
  122. /package/docs/archive/{2026-07-01-dm-auto-space.md → applicative-profiles/2026-07-01-dm-auto-space.md} +0 -0
@@ -11,6 +11,7 @@ import type { ModelCapabilityKey } from "../agent/model-capabilities.js";
11
11
  import { registerPermission } from "../core/permissions.js";
12
12
  import type { Db } from "../storage/db.js";
13
13
  import type {
14
+ CapabilityHandler,
14
15
  CliDef,
15
16
  ConfigDef,
16
17
  ConnectionCategory,
@@ -63,6 +64,7 @@ export class MercuryExtensionAPIImpl implements MercuryExtensionAPI {
63
64
  jobs: new Map(),
64
65
  configs: new Map(),
65
66
  widgets: [],
67
+ capabilities: new Map(),
66
68
  envVars: [],
67
69
  };
68
70
  }
@@ -235,6 +237,25 @@ export class MercuryExtensionAPIImpl implements MercuryExtensionAPI {
235
237
  this.meta.connection = def;
236
238
  }
237
239
 
240
+ capability(name: string, handler: CapabilityHandler): void {
241
+ if (!name || !/^[a-z0-9][a-z0-9-]*$/.test(name)) {
242
+ throw new Error(
243
+ `Extension "${this.name}": capability() requires a lowercase alphanumeric name`,
244
+ );
245
+ }
246
+ if (typeof handler !== "function") {
247
+ throw new Error(
248
+ `Extension "${this.name}": capability("${name}") requires a handler function`,
249
+ );
250
+ }
251
+ if (this.meta.capabilities.has(name)) {
252
+ throw new Error(
253
+ `Extension "${this.name}": capability "${name}" already registered`,
254
+ );
255
+ }
256
+ this.meta.capabilities.set(name, handler);
257
+ }
258
+
238
259
  get store(): ExtensionStore {
239
260
  return {
240
261
  get: (key: string) => this.db.getExtState(this.name, key),
@@ -14,6 +14,7 @@ import { MercuryExtensionAPIImpl } from "./api.js";
14
14
  import type { ConfigRegistry } from "./config-registry.js";
15
15
  import { RESERVED_EXTENSION_NAMES } from "./reserved.js";
16
16
  import type {
17
+ CapabilityHandler,
17
18
  EventHandler,
18
19
  ExtensionMeta,
19
20
  JobDef,
@@ -182,6 +183,20 @@ export class ExtensionRegistry {
182
183
  return this.list().filter((ext) => ext.clis.length > 0);
183
184
  }
184
185
 
186
+ /**
187
+ * Find a registered host-side capability handler by name, with its owning
188
+ * extension. Returns undefined when no extension registered it.
189
+ */
190
+ getCapability(
191
+ name: string,
192
+ ): { ext: ExtensionMeta; handler: CapabilityHandler } | undefined {
193
+ for (const ext of this.extensions.values()) {
194
+ const handler = ext.capabilities.get(name);
195
+ if (handler) return { ext, handler };
196
+ }
197
+ return undefined;
198
+ }
199
+
185
200
  /** Get all env var source names claimed by extensions (for passthrough filtering). */
186
201
  getClaimedEnvSources(): Set<string> {
187
202
  const sources = new Set<string>();
@@ -187,6 +187,45 @@ export interface WidgetDef {
187
187
  render: (ctx: MercuryExtensionContext) => string;
188
188
  }
189
189
 
190
+ // ---------------------------------------------------------------------------
191
+ // Capabilities — host-side broker actions invoked from the container
192
+ // ---------------------------------------------------------------------------
193
+
194
+ /** A single broker request, dispatched from `POST /api/capability/:name/:action`. */
195
+ export interface CapabilityRequest {
196
+ /** Capability name (matches the required permission). */
197
+ name: string;
198
+ /** Sub-action within the capability (e.g. "book", "cancel"). */
199
+ action: string;
200
+ /**
201
+ * The authoritative, token-derived caller id. Trustworthy for ownership
202
+ * checks — NOT a container-supplied argument.
203
+ */
204
+ callerId: string;
205
+ /** The space the call was made from. */
206
+ spaceId: string;
207
+ /** Parsed JSON request body (or null). */
208
+ body: unknown;
209
+ }
210
+
211
+ /** Result returned by a capability handler. */
212
+ export interface CapabilityResult {
213
+ /** HTTP status to return (default 200). */
214
+ status?: number;
215
+ /** JSON-serializable response payload. */
216
+ data: unknown;
217
+ }
218
+
219
+ /**
220
+ * Host-side handler for a capability. Runs in the Mercury host process with the
221
+ * full extension context (credentials in host storage stay on the host). The
222
+ * container never sees secrets — only the returned `data`.
223
+ */
224
+ export type CapabilityHandler = (
225
+ req: CapabilityRequest,
226
+ ctx: MercuryExtensionContext,
227
+ ) => Promise<CapabilityResult>;
228
+
190
229
  // ---------------------------------------------------------------------------
191
230
  // Store
192
231
  // ---------------------------------------------------------------------------
@@ -310,6 +349,20 @@ export interface MercuryExtensionAPI {
310
349
  */
311
350
  connection(def: ConnectionDef): void;
312
351
 
352
+ /**
353
+ * Register a host-side capability handler, invoked from the container via
354
+ * `mrctl capability <name> <action> <json>` → `POST /api/capability/:name/:action`.
355
+ * The caller must hold the `<name>` permission. Credentials used by the
356
+ * handler stay on the host and never enter the agent container.
357
+ *
358
+ * @example
359
+ * mercury.capability("barber", async (req, ctx) => {
360
+ * if (req.action === "book") return { data: await book(req.callerId, req.body) };
361
+ * return { status: 400, data: { error: "unknown action" } };
362
+ * });
363
+ */
364
+ capability(name: string, handler: CapabilityHandler): void;
365
+
313
366
  /** Scoped key-value store for persistent extension state. */
314
367
  readonly store: ExtensionStore;
315
368
  }
@@ -448,6 +501,8 @@ export interface ExtensionMeta {
448
501
  configs: Map<string, ConfigDef>;
449
502
  /** Dashboard widgets. */
450
503
  widgets: WidgetDef[];
504
+ /** Host-side capability handlers, keyed by capability name. */
505
+ capabilities: Map<string, CapabilityHandler>;
451
506
  /** Declared environment variables. */
452
507
  envVars: EnvDef[];
453
508
  /** Personal service connection metadata, if declared via `mercury.connection()`. */
package/src/main.ts CHANGED
@@ -34,6 +34,11 @@ import { WhatsAppBridge } from "./bridges/whatsapp.js";
34
34
  import { createChatShim } from "./chat-shim.js";
35
35
  import { loadConfig, resolveProjectPath } from "./config.js";
36
36
  import { createMessageHandler } from "./core/handler.js";
37
+ import { setActiveProfileMemberPermissions } from "./core/permissions.js";
38
+ import {
39
+ loadActiveProfile,
40
+ setActiveProfileSystemPrompt,
41
+ } from "./core/profiles.js";
37
42
  import { MercuryCoreRuntime } from "./core/runtime.js";
38
43
  import { runStorageCleanup } from "./core/storage-cleanup.js";
39
44
  import { isOverQuota } from "./core/storage-guard.js";
@@ -214,6 +219,20 @@ async function main() {
214
219
  ]);
215
220
  logger.info("Extensions loaded", { count: registry.size });
216
221
 
222
+ // Activate the applicative profile (project-wide member permission scoping).
223
+ // Done after extensions load so extension-registered permissions (e.g. a
224
+ // profile's own CLI permission) are recognized as valid.
225
+ const activeProfile = loadActiveProfile(resolveProjectPath(config.dataDir));
226
+ if (activeProfile) {
227
+ setActiveProfileMemberPermissions(activeProfile.memberPermissions ?? null);
228
+ setActiveProfileSystemPrompt(activeProfile.systemPrompt ?? null);
229
+ logger.info("Applicative profile active", {
230
+ profile: activeProfile.name,
231
+ memberPermissions:
232
+ activeProfile.memberPermissions?.join(",") ?? "(unscoped)",
233
+ });
234
+ }
235
+
217
236
  logExtensionCapabilityMismatches(
218
237
  registry.list(),
219
238
  config.resolvedModelChainCapabilities,
@@ -1,15 +0,0 @@
1
- ---
2
- slug: dm-auto-space
3
- shipped: 2026-07-01
4
- ---
5
-
6
- ## ROADMAP changes
7
-
8
- **Remove from:** Next (table row where Item = dm-auto-space)
9
-
10
- **Also pull:** none
11
-
12
- **Add to Shipped table** (insert at top, after header row):
13
- | **dm-auto-space** | 2026-07-01 | Auto-create space per customer DM with rate limits and dashboard controls |
14
-
15
- **Milestone Map:** none