mercury-agent 0.4.7 → 0.4.8
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/docs/auth/dashboard.md +28 -28
- package/examples/extensions/voice-synth/index.ts +94 -94
- package/package.json +1 -1
- package/src/adapters/whatsapp.ts +635 -632
- package/src/agent/model-capabilities.ts +231 -231
- package/src/bridges/discord.ts +178 -178
- package/src/bridges/slack.ts +179 -179
- package/src/bridges/teams.ts +162 -162
- package/src/bridges/telegram.ts +579 -579
- package/src/cli/mercury.ts +2536 -2536
- package/src/cli/whatsapp-auth.ts +263 -260
- package/src/config.ts +316 -316
- package/src/core/permissions.ts +196 -196
- package/src/core/router.ts +191 -191
- package/src/core/routes/chat.ts +175 -175
- package/src/core/routes/dashboard.ts +2491 -2491
- package/src/core/routes/messages.ts +37 -37
- package/src/core/routes/mutes.ts +95 -95
- package/src/core/routes/roles.ts +135 -135
- package/src/core/runtime.ts +1140 -1140
- package/src/core/task-scheduler.ts +139 -139
- package/src/extensions/catalog.ts +117 -117
- package/src/extensions/hooks.ts +161 -161
- package/src/extensions/installer.ts +306 -306
- package/src/extensions/loader.ts +271 -271
- package/src/extensions/permission-guard.ts +52 -52
- package/src/server.ts +391 -391
- package/src/storage/db.ts +1625 -1625
- package/src/storage/pi-auth.ts +95 -95
- package/src/tts/azure.ts +52 -52
- package/src/tts/synthesize.ts +133 -133
package/src/extensions/hooks.ts
CHANGED
|
@@ -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
|
+
}
|