diff --git a/lib/__tests__/plugin-host-features.test.ts b/lib/__tests__/plugin-host-features.test.ts index 9e0dc0abf..bea136c3a 100644 --- a/lib/__tests__/plugin-host-features.test.ts +++ b/lib/__tests__/plugin-host-features.test.ts @@ -85,21 +85,21 @@ describe('host bridge', () => { const plugin = { id: 'p', name: 'P', version: '1.0.0', permissions: [], settings: {}, settingsSchema: {} } as never; const instance = createBackgroundInstance({ plugin, tier: 'privileged', code: '', locale: 'en' }); const pending = (instance as unknown as { pendingInit: { hostFeatures?: unknown } | null }).pendingInit; - expect(pending?.hostFeatures).toEqual({ externalSenderLabel: true, externalRecipientWarning: false, noThirdPartyTraffic: false }); + expect(pending?.hostFeatures).toEqual(withAssistant({ externalSenderLabel: true, externalRecipientWarning: false, noThirdPartyTraffic: false })); // Before init has gone out a change lands in the payload ... setExternalMail({}); - expect(pending?.hostFeatures).toEqual({ externalSenderLabel: false, externalRecipientWarning: false, noThirdPartyTraffic: false }); + expect(pending?.hostFeatures).toEqual(withAssistant({ externalSenderLabel: false, externalRecipientWarning: false, noThirdPartyTraffic: false })); // ... after it, as a host-change message. const sent: unknown[] = []; (instance as unknown as { pendingInit: unknown }).pendingInit = null; (instance as unknown as { send: (m: unknown) => void }).send = (m) => sent.push(m); setExternalMail({ recipientWarning: true }); - expect(sent).toEqual([{ type: 'host-change', features: { externalSenderLabel: false, externalRecipientWarning: true, noThirdPartyTraffic: false } }]); + expect(sent).toEqual([{ type: 'host-change', features: withAssistant({ externalSenderLabel: false, externalRecipientWarning: true, noThirdPartyTraffic: false }) }]); // The admin turns third-party traffic off: running plugins hear it. setNoThirdPartyTraffic(true); - expect(sent[1]).toEqual({ type: 'host-change', features: { externalSenderLabel: false, externalRecipientWarning: true, noThirdPartyTraffic: true } }); + expect(sent[1]).toEqual({ type: 'host-change', features: withAssistant({ externalSenderLabel: false, externalRecipientWarning: true, noThirdPartyTraffic: true }) }); setNoThirdPartyTraffic(false); instance.destroy(); }, 30_000); @@ -165,5 +165,12 @@ describe('api.host in the sandbox runtime', () => { expect(kept.externalSenderLabel).toBe(true); expect((kept as { noThirdPartyTraffic?: boolean }).noThirdPartyTraffic).toBe(true); expect(changes).toEqual([{ externalSenderLabel: true, externalRecipientWarning: false, noThirdPartyTraffic: true }]); + + // The assistant's flags (lib/plugin-sandbox/assistant) reach the plugin too. + expect((kept as { sidePanel?: boolean }).sidePanel).toBeUndefined(); + post(hostWindow, HOST_ORIGIN, { type: 'host-change', features: { externalSenderLabel: true, externalRecipientWarning: false, noThirdPartyTraffic: true, sidePanel: true } }); + expect((kept as { sidePanel?: boolean }).sidePanel).toBe(true); + post(hostWindow, HOST_ORIGIN, { type: 'host-change', features: { externalSenderLabel: true, externalRecipientWarning: false, noThirdPartyTraffic: true, sidePanel: 'yes' } }); + expect((kept as { sidePanel?: boolean }).sidePanel).toBe(false); }, 30_000); }); diff --git a/lib/plugin-sandbox/runtime.tsx b/lib/plugin-sandbox/runtime.tsx index 63b0b361a..ab741c7a9 100644 --- a/lib/plugin-sandbox/runtime.tsx +++ b/lib/plugin-sandbox/runtime.tsx @@ -161,6 +161,11 @@ function applyHostFeatures(next: HostFeaturesView | undefined): void { hostFeaturesState.externalRecipientWarning = next.externalRecipientWarning === true; // A host older than 1.20 doesn't send it, and never let such a plugin run in the mode. hostFeaturesState.noThirdPartyTraffic = next.noThirdPartyTraffic === true; + // The AI assistant's flags (lib/plugin-sandbox/assistant): whatever the host names, true or false. + const flags = hostFeaturesState as unknown as Record; + for (const [key, value] of Object.entries(next)) { + if (key !== 'externalSenderLabel' && key !== 'externalRecipientWarning' && key !== 'noThirdPartyTraffic') flags[key] = value === true; + } for (const listener of hostFeatureListeners) { try { listener(hostFeaturesState); } catch (err) { console.error('[plugin] host feature listener failed', err); } }