mercury-agent 0.4.7 → 0.4.9

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 (36) hide show
  1. package/container/Dockerfile.power +1 -1
  2. package/docs/auth/dashboard.md +28 -28
  3. package/docs/container-lifecycle.md +4 -4
  4. package/examples/extensions/voice-synth/index.ts +94 -94
  5. package/examples/extensions/voice-transcribe/scripts/transcribe.py +1 -1
  6. package/package.json +1 -1
  7. package/resources/templates/mercury.example.yaml +1 -1
  8. package/src/adapters/whatsapp.ts +635 -632
  9. package/src/agent/container-runner.ts +3 -1
  10. package/src/agent/model-capabilities.ts +231 -231
  11. package/src/bridges/discord.ts +178 -178
  12. package/src/bridges/slack.ts +179 -179
  13. package/src/bridges/teams.ts +162 -162
  14. package/src/bridges/telegram.ts +579 -579
  15. package/src/cli/mercury.ts +2551 -2536
  16. package/src/cli/whatsapp-auth.ts +263 -260
  17. package/src/config.ts +316 -316
  18. package/src/core/permissions.ts +196 -196
  19. package/src/core/router.ts +191 -191
  20. package/src/core/routes/chat.ts +175 -175
  21. package/src/core/routes/dashboard.ts +2491 -2491
  22. package/src/core/routes/messages.ts +37 -37
  23. package/src/core/routes/mutes.ts +95 -95
  24. package/src/core/routes/roles.ts +135 -135
  25. package/src/core/runtime.ts +1140 -1140
  26. package/src/core/task-scheduler.ts +139 -139
  27. package/src/extensions/catalog.ts +117 -117
  28. package/src/extensions/hooks.ts +161 -161
  29. package/src/extensions/installer.ts +306 -306
  30. package/src/extensions/loader.ts +271 -271
  31. package/src/extensions/permission-guard.ts +52 -52
  32. package/src/server.ts +391 -391
  33. package/src/storage/db.ts +1625 -1625
  34. package/src/storage/pi-auth.ts +95 -95
  35. package/src/tts/azure.ts +52 -52
  36. package/src/tts/synthesize.ts +133 -133
@@ -1,161 +1,161 @@
1
- /**
2
- * Hook dispatcher — emits lifecycle events and dispatches to extension handlers.
3
- *
4
- * Handles mutation semantics for before_container and after_container events.
5
- * Errors in handlers are caught and logged — never crash Mercury.
6
- */
7
-
8
- import type { Logger } from "../logger.js";
9
- import type { EgressFile } from "../types.js";
10
- import type { ExtensionRegistry } from "./loader.js";
11
- import type {
12
- AfterContainerResult,
13
- BeforeContainerResult,
14
- MercuryEvents,
15
- MercuryExtensionContext,
16
- } from "./types.js";
17
-
18
- export class HookDispatcher {
19
- constructor(
20
- private readonly registry: ExtensionRegistry,
21
- private readonly log: Logger,
22
- ) {}
23
-
24
- /**
25
- * Emit a non-mutating event (startup, shutdown, workspace_init).
26
- * Runs all handlers in load order. Errors are caught and logged.
27
- */
28
- async emit<E extends "startup" | "shutdown" | "workspace_init">(
29
- event: E,
30
- data: MercuryEvents[E],
31
- ctx: MercuryExtensionContext,
32
- ): Promise<void> {
33
- const handlers = this.registry.getHookHandlers(event);
34
- for (const handler of handlers) {
35
- try {
36
- await handler(data, ctx);
37
- } catch (err) {
38
- this.log.error(
39
- `Hook "${event}" handler failed: ${err instanceof Error ? err.message : String(err)}`,
40
- );
41
- }
42
- }
43
- }
44
-
45
- /**
46
- * Emit before_container event with mutation support.
47
- *
48
- * Mutation semantics:
49
- * - systemPrompt: concatenated across handlers (newline-separated)
50
- * - promptAppend: concatenated across handlers (newline-separated)
51
- * - env: merged (last-write-wins on key conflict)
52
- * - block: first handler to block stops the chain
53
- */
54
- async emitBeforeContainer(
55
- data: MercuryEvents["before_container"],
56
- ctx: MercuryExtensionContext,
57
- ): Promise<BeforeContainerResult | undefined> {
58
- const handlers = this.registry.getHookHandlers("before_container");
59
- if (handlers.length === 0) return undefined;
60
-
61
- const systemPromptParts: string[] = [];
62
- const promptAppendParts: string[] = [];
63
- let env: Record<string, string> = {};
64
- let hasMutations = false;
65
-
66
- for (const handler of handlers) {
67
- try {
68
- const result = await handler(data, ctx);
69
- if (!result) continue;
70
-
71
- hasMutations = true;
72
-
73
- if (result.block) {
74
- return { block: result.block };
75
- }
76
- if (result.systemPrompt) {
77
- systemPromptParts.push(result.systemPrompt);
78
- }
79
- if (result.promptAppend) {
80
- promptAppendParts.push(result.promptAppend);
81
- }
82
- if (result.env) {
83
- env = { ...env, ...result.env };
84
- }
85
- } catch (err) {
86
- this.log.error(
87
- `Hook "before_container" handler failed: ${err instanceof Error ? err.message : String(err)}`,
88
- );
89
- return {
90
- block: {
91
- reason: `Extension hook failed: ${err instanceof Error ? err.message : String(err)}`,
92
- },
93
- };
94
- }
95
- }
96
-
97
- if (!hasMutations) return undefined;
98
-
99
- return {
100
- ...(systemPromptParts.length > 0
101
- ? { systemPrompt: systemPromptParts.join("\n") }
102
- : {}),
103
- ...(promptAppendParts.length > 0
104
- ? { promptAppend: promptAppendParts.join("\n") }
105
- : {}),
106
- ...(Object.keys(env).length > 0 ? { env } : {}),
107
- };
108
- }
109
-
110
- /**
111
- * Emit after_container event with mutation support.
112
- *
113
- * Mutation semantics:
114
- * - reply: last handler to return a reply wins
115
- * - suppress: any handler returning true suppresses
116
- * - files: arrays concatenated in handler registration order (append-only)
117
- */
118
- async emitAfterContainer(
119
- data: MercuryEvents["after_container"],
120
- ctx: MercuryExtensionContext,
121
- ): Promise<AfterContainerResult | undefined> {
122
- const handlers = this.registry.getHookHandlers("after_container");
123
- if (handlers.length === 0) return undefined;
124
-
125
- let reply: string | undefined;
126
- let suppress = false;
127
- const fileParts: EgressFile[] = [];
128
- let hasMutations = false;
129
-
130
- for (const handler of handlers) {
131
- try {
132
- const result = await handler(data, ctx);
133
- if (!result) continue;
134
-
135
- hasMutations = true;
136
-
137
- if (result.reply !== undefined) {
138
- reply = result.reply;
139
- }
140
- if (result.suppress) {
141
- suppress = true;
142
- }
143
- if (result.files?.length) {
144
- fileParts.push(...result.files);
145
- }
146
- } catch (err) {
147
- this.log.error(
148
- `Hook "after_container" handler failed: ${err instanceof Error ? err.message : String(err)}`,
149
- );
150
- }
151
- }
152
-
153
- if (!hasMutations) return undefined;
154
-
155
- return {
156
- ...(reply !== undefined ? { reply } : {}),
157
- ...(suppress ? { suppress } : {}),
158
- ...(fileParts.length > 0 ? { files: fileParts } : {}),
159
- };
160
- }
161
- }
1
+ /**
2
+ * Hook dispatcher — emits lifecycle events and dispatches to extension handlers.
3
+ *
4
+ * Handles mutation semantics for before_container and after_container events.
5
+ * Errors in handlers are caught and logged — never crash Mercury.
6
+ */
7
+
8
+ import type { Logger } from "../logger.js";
9
+ import type { EgressFile } from "../types.js";
10
+ import type { ExtensionRegistry } from "./loader.js";
11
+ import type {
12
+ AfterContainerResult,
13
+ BeforeContainerResult,
14
+ MercuryEvents,
15
+ MercuryExtensionContext,
16
+ } from "./types.js";
17
+
18
+ export class HookDispatcher {
19
+ constructor(
20
+ private readonly registry: ExtensionRegistry,
21
+ private readonly log: Logger,
22
+ ) {}
23
+
24
+ /**
25
+ * Emit a non-mutating event (startup, shutdown, workspace_init).
26
+ * Runs all handlers in load order. Errors are caught and logged.
27
+ */
28
+ async emit<E extends "startup" | "shutdown" | "workspace_init">(
29
+ event: E,
30
+ data: MercuryEvents[E],
31
+ ctx: MercuryExtensionContext,
32
+ ): Promise<void> {
33
+ const handlers = this.registry.getHookHandlers(event);
34
+ for (const handler of handlers) {
35
+ try {
36
+ await handler(data, ctx);
37
+ } catch (err) {
38
+ this.log.error(
39
+ `Hook "${event}" handler failed: ${err instanceof Error ? err.message : String(err)}`,
40
+ );
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Emit before_container event with mutation support.
47
+ *
48
+ * Mutation semantics:
49
+ * - systemPrompt: concatenated across handlers (newline-separated)
50
+ * - promptAppend: concatenated across handlers (newline-separated)
51
+ * - env: merged (last-write-wins on key conflict)
52
+ * - block: first handler to block stops the chain
53
+ */
54
+ async emitBeforeContainer(
55
+ data: MercuryEvents["before_container"],
56
+ ctx: MercuryExtensionContext,
57
+ ): Promise<BeforeContainerResult | undefined> {
58
+ const handlers = this.registry.getHookHandlers("before_container");
59
+ if (handlers.length === 0) return undefined;
60
+
61
+ const systemPromptParts: string[] = [];
62
+ const promptAppendParts: string[] = [];
63
+ let env: Record<string, string> = {};
64
+ let hasMutations = false;
65
+
66
+ for (const handler of handlers) {
67
+ try {
68
+ const result = await handler(data, ctx);
69
+ if (!result) continue;
70
+
71
+ hasMutations = true;
72
+
73
+ if (result.block) {
74
+ return { block: result.block };
75
+ }
76
+ if (result.systemPrompt) {
77
+ systemPromptParts.push(result.systemPrompt);
78
+ }
79
+ if (result.promptAppend) {
80
+ promptAppendParts.push(result.promptAppend);
81
+ }
82
+ if (result.env) {
83
+ env = { ...env, ...result.env };
84
+ }
85
+ } catch (err) {
86
+ this.log.error(
87
+ `Hook "before_container" handler failed: ${err instanceof Error ? err.message : String(err)}`,
88
+ );
89
+ return {
90
+ block: {
91
+ reason: `Extension hook failed: ${err instanceof Error ? err.message : String(err)}`,
92
+ },
93
+ };
94
+ }
95
+ }
96
+
97
+ if (!hasMutations) return undefined;
98
+
99
+ return {
100
+ ...(systemPromptParts.length > 0
101
+ ? { systemPrompt: systemPromptParts.join("\n") }
102
+ : {}),
103
+ ...(promptAppendParts.length > 0
104
+ ? { promptAppend: promptAppendParts.join("\n") }
105
+ : {}),
106
+ ...(Object.keys(env).length > 0 ? { env } : {}),
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Emit after_container event with mutation support.
112
+ *
113
+ * Mutation semantics:
114
+ * - reply: last handler to return a reply wins
115
+ * - suppress: any handler returning true suppresses
116
+ * - files: arrays concatenated in handler registration order (append-only)
117
+ */
118
+ async emitAfterContainer(
119
+ data: MercuryEvents["after_container"],
120
+ ctx: MercuryExtensionContext,
121
+ ): Promise<AfterContainerResult | undefined> {
122
+ const handlers = this.registry.getHookHandlers("after_container");
123
+ if (handlers.length === 0) return undefined;
124
+
125
+ let reply: string | undefined;
126
+ let suppress = false;
127
+ const fileParts: EgressFile[] = [];
128
+ let hasMutations = false;
129
+
130
+ for (const handler of handlers) {
131
+ try {
132
+ const result = await handler(data, ctx);
133
+ if (!result) continue;
134
+
135
+ hasMutations = true;
136
+
137
+ if (result.reply !== undefined) {
138
+ reply = result.reply;
139
+ }
140
+ if (result.suppress) {
141
+ suppress = true;
142
+ }
143
+ if (result.files?.length) {
144
+ fileParts.push(...result.files);
145
+ }
146
+ } catch (err) {
147
+ this.log.error(
148
+ `Hook "after_container" handler failed: ${err instanceof Error ? err.message : String(err)}`,
149
+ );
150
+ }
151
+ }
152
+
153
+ if (!hasMutations) return undefined;
154
+
155
+ return {
156
+ ...(reply !== undefined ? { reply } : {}),
157
+ ...(suppress ? { suppress } : {}),
158
+ ...(fileParts.length > 0 ? { files: fileParts } : {}),
159
+ };
160
+ }
161
+ }