mercury-agent 0.7.0-beta.0 → 0.7.0
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/extensions.md
CHANGED
|
@@ -85,6 +85,11 @@ mercury.permission({ defaultRoles: ["admin", "member"] });
|
|
|
85
85
|
- `defaultRoles` — roles that get this permission by default
|
|
86
86
|
- `admin` always gets all permissions automatically
|
|
87
87
|
- Per-space overrides in `space_config` take precedence
|
|
88
|
+
- **Applicative profiles override `defaultRoles`**: when the active profile
|
|
89
|
+
defines `member_permissions`, that list is exhaustive for the `member` role —
|
|
90
|
+
an extension is only usable by members if its name appears there. Mercury logs
|
|
91
|
+
a startup warning for each loaded extension whose `defaultRoles` include
|
|
92
|
+
`member` but which the profile excludes.
|
|
88
93
|
|
|
89
94
|
Can only be called once per extension.
|
|
90
95
|
|
|
@@ -309,6 +309,7 @@ function localTimeoutMs(): number {
|
|
|
309
309
|
type HookCtx = {
|
|
310
310
|
db: { getSpaceConfig: (spaceId: string, key: string) => string | null };
|
|
311
311
|
log: {
|
|
312
|
+
debug: (msg: string, extra?: unknown) => void;
|
|
312
313
|
info: (msg: string, extra?: unknown) => void;
|
|
313
314
|
warn: (msg: string, extra?: unknown) => void;
|
|
314
315
|
error: (msg: string, extra?: unknown) => void;
|
|
@@ -399,6 +400,11 @@ export default function (mercury: {
|
|
|
399
400
|
|
|
400
401
|
mercury.on("before_container", async (event, ctx) => {
|
|
401
402
|
if (!ctx.hasCallerPermission(event.spaceId, event.callerId, EXT)) {
|
|
403
|
+
ctx.log.debug("Skipping voice transcription: caller lacks permission", {
|
|
404
|
+
extension: EXT,
|
|
405
|
+
spaceId: event.spaceId,
|
|
406
|
+
callerId: event.callerId,
|
|
407
|
+
});
|
|
402
408
|
return undefined;
|
|
403
409
|
}
|
|
404
410
|
|
package/package.json
CHANGED
|
@@ -244,7 +244,9 @@ async function runSingleAgent(
|
|
|
244
244
|
};
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
// --no-approve: the trust store dir (~/.pi/agent) is mounted read-only in
|
|
248
|
+
// containers and any trust-store access acquires a lock there (EROFS crash).
|
|
249
|
+
const args: string[] = ["--mode", "json", "-p", "--no-session", "--no-approve"];
|
|
248
250
|
if (agent.model) args.push("--model", agent.model);
|
|
249
251
|
if (agent.tools && agent.tools.length > 0) args.push("--tools", agent.tools.join(","));
|
|
250
252
|
|
|
@@ -835,6 +835,13 @@ function invokePiOnce(
|
|
|
835
835
|
"--print",
|
|
836
836
|
"--mode",
|
|
837
837
|
"json",
|
|
838
|
+
// Skip pi's project-trust store entirely. The store lives in
|
|
839
|
+
// /home/mercury/.pi/agent (mounted :ro) and even a read locks it via
|
|
840
|
+
// proper-lockfile (mkdir trust.json.lock → EROFS). Workspaces with
|
|
841
|
+
// trust-requiring resources already resolved to "untrusted" in
|
|
842
|
+
// non-interactive mode, and without such resources nothing trust-gated
|
|
843
|
+
// loads — so --no-approve preserves behavior either way.
|
|
844
|
+
"--no-approve",
|
|
838
845
|
...sessionArgs,
|
|
839
846
|
"--provider",
|
|
840
847
|
provider,
|
package/src/core/profiles.ts
CHANGED
|
@@ -233,6 +233,31 @@ export function getActiveProfilePrompt(): string | null {
|
|
|
233
233
|
return activeProfilePromptValue;
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Warn about loaded extensions whose permission grants `member` by default but
|
|
238
|
+
* whose permission name is missing from the active profile's exhaustive
|
|
239
|
+
* `member_permissions` list. The override itself is by design (profiles scope
|
|
240
|
+
* customer-facing members to least privilege); the warning exists because the
|
|
241
|
+
* resulting per-message hook skips are otherwise invisible in the logs.
|
|
242
|
+
*/
|
|
243
|
+
export function logProfileMemberPermissionExclusions(
|
|
244
|
+
extensions: Array<{ name: string; permission?: { defaultRoles: string[] } }>,
|
|
245
|
+
memberPermissions: string[] | null,
|
|
246
|
+
log: { warn: (msg: string, obj?: Record<string, unknown>) => void },
|
|
247
|
+
): void {
|
|
248
|
+
if (!Array.isArray(memberPermissions)) return;
|
|
249
|
+
// Match toPermissionSet's trimming so a padded entry doesn't false-positive.
|
|
250
|
+
const granted = new Set(memberPermissions.map((s) => s.trim()));
|
|
251
|
+
for (const ext of extensions) {
|
|
252
|
+
if (!ext.permission?.defaultRoles.includes("member")) continue;
|
|
253
|
+
if (granted.has(ext.name)) continue;
|
|
254
|
+
log.warn(
|
|
255
|
+
`Extension "${ext.name}" grants role "member" by default, but the active profile's member_permissions excludes it — member callers cannot use it unless a space-level permission override grants it. Add "${ext.name}" to the profile's member_permissions (or the deployed active-profile.json) to enable it.`,
|
|
256
|
+
{ extension: ext.name },
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
236
261
|
/**
|
|
237
262
|
* Throw if any of the profile's declared `capabilities` is not present as an
|
|
238
263
|
* installed extension directory under `<dataDir>/extensions`. Capabilities may
|
package/src/main.ts
CHANGED
|
@@ -35,7 +35,11 @@ import { createChatShim } from "./chat-shim.js";
|
|
|
35
35
|
import { loadConfig, resolveProjectPath } from "./config.js";
|
|
36
36
|
import { createMessageHandler } from "./core/handler.js";
|
|
37
37
|
import { setActiveProfileMemberPermissions } from "./core/permissions.js";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
loadActiveProfile,
|
|
40
|
+
logProfileMemberPermissionExclusions,
|
|
41
|
+
setActiveProfilePrompt,
|
|
42
|
+
} from "./core/profiles.js";
|
|
39
43
|
import { MercuryCoreRuntime } from "./core/runtime.js";
|
|
40
44
|
import { runStorageCleanup } from "./core/storage-cleanup.js";
|
|
41
45
|
import { isOverQuota } from "./core/storage-guard.js";
|
|
@@ -228,6 +232,11 @@ async function main() {
|
|
|
228
232
|
memberPermissions:
|
|
229
233
|
activeProfile.memberPermissions?.join(",") ?? "(unscoped)",
|
|
230
234
|
});
|
|
235
|
+
logProfileMemberPermissionExclusions(
|
|
236
|
+
registry.list(),
|
|
237
|
+
activeProfile.memberPermissions ?? null,
|
|
238
|
+
logger,
|
|
239
|
+
);
|
|
231
240
|
}
|
|
232
241
|
|
|
233
242
|
logExtensionCapabilityMismatches(
|