From abd2c0d5eb28456e78ee7e19829e0a598045cffe Mon Sep 17 00:00:00 2001 From: bruno moniz Date: Fri, 19 Jun 2026 09:42:45 -0300 Subject: [PATCH] feat(memoria): curinga '*' em PROFILE_INJECT_ACCOUNTS (injecao para todas as contas) Mantem fail-closed: accountId ausente nao injeta nem com '*'. Cada conta so ve os proprios fatos. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/__tests__/profile-injection.test.ts | 36 +++++++++++++++++++++++++ src/mcp-account-config.ts | 9 ++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/__tests__/profile-injection.test.ts b/src/__tests__/profile-injection.test.ts index bf2f50c..3e297bf 100644 --- a/src/__tests__/profile-injection.test.ts +++ b/src/__tests__/profile-injection.test.ts @@ -110,6 +110,42 @@ test("fail-closed: accountId null → base pura, loadFacts NÃO chamado", async assert.equal(out, FRIEND_INSTRUCTIONS); }); +// --- curinga "*": todas as contas, mas ainda fail-closed em accountId ausente - +test('curinga "*": qualquer accountId presente injeta os PRÓPRIOS fatos', async () => { + const spyA = spyLoad(); + const a = await resolveInstructions({ + owner: false, + injectEnabled: true, + accountId: "acct_qualquer", + allowlist: ["*"], + loadFacts: spyA.fn, + }); + assert.ok(a.includes("FATO_DA_CONTA_acct_qualquer"), "* injeta os fatos da conta"); + assert.deepEqual(spyA.calls(), ["acct_qualquer"], "carregou só a própria conta"); +}); + +test('curinga "*": accountId null/ausente → base pura (fail-closed mesmo com *)', async () => { + const out = await resolveInstructions({ + owner: false, + injectEnabled: true, + accountId: null, + allowlist: ["*"], + loadFacts: neverCalled(), + }); + assert.equal(out, FRIEND_INSTRUCTIONS, "* não burla o fail-closed de accountId ausente"); +}); + +test('curinga "*": flag off → base pura (não injeta)', async () => { + const out = await resolveInstructions({ + owner: false, + injectEnabled: false, + accountId: "acct_qualquer", + allowlist: ["*"], + loadFacts: neverCalled(), + }); + assert.equal(out, FRIEND_INSTRUCTIONS); +}); + test("fail-closed: accountId '' (vazio) → base pura, loadFacts NÃO chamado", async () => { const out = await resolveInstructions({ owner: true, diff --git a/src/mcp-account-config.ts b/src/mcp-account-config.ts index 17b3547..b33996b 100644 --- a/src/mcp-account-config.ts +++ b/src/mcp-account-config.ts @@ -95,10 +95,13 @@ export async function resolveInstructions(args: { const render = args.render ?? renderProfile; const budget = args.budget ?? PROFILE_CHAR_BUDGET; - // FAIL-CLOSED: injeta só com flag ligada, accountId presente E na allowlist. - // accountId vazio/ausente ou fora do allowlist ⇒ nunca toca loadFacts. + // FAIL-CLOSED: injeta só com flag ligada, accountId presente E (allowlist com o + // curinga "*" = todas as contas, OU a conta explicitamente na allowlist). + // accountId vazio/ausente ⇒ nunca toca loadFacts, mesmo com "*" (fail-closed). const allowed = - args.injectEnabled && !!args.accountId && args.allowlist.includes(args.accountId); + args.injectEnabled && + !!args.accountId && + (args.allowlist.includes("*") || args.allowlist.includes(args.accountId)); // ISOLAMENTO: cada conta carrega SÓ os próprios fatos (loadFacts(accountId)). const profileBlock = allowed