openvibers/openvibe.network Commits
Actor runs its model calls through OpenVibe.AI (plan L55 C3) (#478)
Changes
@@@ -471,6 +471,10 @@ const DEFAULT_GRANTS = [// OpenAI-compatible harnesses). Nothing else of AI's.['codes', 'ai.credential.manage', 'openvibe.ai', []],['codes', 'ai.run.create', 'openvibe.ai', ['ai.messages', 'ai.complete']],++ // Actor (plan L55 C3): a person chooses the model their tasks run on, with their own provider key when they have++ // one (the key lives in OpenVibe.AI, never in Actor). Actor runs the call through AI's OpenAI-compatible endpoint++ // (workflow ai.complete), the same one Codes' harnesses use. Nothing else of AI's.++ ['actor', 'ai.run.create', 'openvibe.ai', ['ai.complete']],// A streamer's daily AI-viewer budget is an AI quota on their attribution (live:user:<id>), which Live sets.['live', 'ai.quota.attribution.manage', 'openvibe.ai', []],// Wave 3: producers publish to OpenVibe.Events (their own source only, enforced by Events).@@@ -76,6 +76,17 @@ const runTokens = require('../server/identity/run-tokens');const codesGrants = await principals.grantsFor(db, 'codes', 'openvibe.ai');assert.deepStrictEqual(codesGrants.map((g) => g.capability).sort(), ['ai.credential.manage', 'ai.run.create']);++ // ── The grants Actor holds for OpenVibe.AI (plan L55 C3) ──++ // A person chooses the model their tasks run on, with their own key (sealed in AI) or OpenVibe's. Actor runs++ // the call through AI's OpenAI-compatible endpoint (workflow ai.complete) and nothing else, and it never++ // holds a key of its own: no consent grant, so it may not store a person's key in AI.++ assert.ok(has('actor', 'ai.run.create', 'openvibe.ai'), 'Actor runs AI\'s OpenAI-compatible endpoint');++ assert.ok(!has('actor', 'ai.credential.manage', 'openvibe.ai'), 'Actor never stores a person\'s key in AI');++ const actorGrant = principals.DEFAULT_GRANTS.find((g) => g[0] === 'actor' && g[1] === 'ai.run.create' && g[2] === 'openvibe.ai');++ assert.deepStrictEqual(actorGrant[3], ['ai.complete'], 'AI\'s OpenAI-compatible workflow only');++ const actorGrants = await principals.grantsFor(db, 'actor', 'openvibe.ai');++ assert.deepStrictEqual(actorGrants.map((g) => g.capability).sort(), ['ai.run.create']);++// ── A run token: the person it acts for, and how long it lives ──let t = await runToken(OWNER);assert.strictEqual(t.status, 200, JSON.stringify(t.body));
@@ -471,6 +471,10 @@ const DEFAULT_GRANTS = [
@@ -471,6 +471,10 @@ const DEFAULT_GRANTS = [
// OpenAI-compatible harnesses). Nothing else of AI's.
// OpenAI-compatible harnesses). Nothing else of AI's.
['codes', 'ai.credential.manage', 'openvibe.ai', []],
['codes', 'ai.credential.manage', 'openvibe.ai', []],
['codes', 'ai.run.create', 'openvibe.ai', ['ai.messages', 'ai.complete']],
['codes', 'ai.run.create', 'openvibe.ai', ['ai.messages', 'ai.complete']],
+ // Actor (plan L55 C3): a person chooses the model their tasks run on, with their own provider key when they have
+ // one (the key lives in OpenVibe.AI, never in Actor). Actor runs the call through AI's OpenAI-compatible endpoint
+ // (workflow ai.complete), the same one Codes' harnesses use. Nothing else of AI's.
+ ['actor', 'ai.run.create', 'openvibe.ai', ['ai.complete']],
// A streamer's daily AI-viewer budget is an AI quota on their attribution (live:user:<id>), which Live sets.
// A streamer's daily AI-viewer budget is an AI quota on their attribution (live:user:<id>), which Live sets.
['live', 'ai.quota.attribution.manage', 'openvibe.ai', []],
['live', 'ai.quota.attribution.manage', 'openvibe.ai', []],
// Wave 3: producers publish to OpenVibe.Events (their own source only, enforced by Events).
// Wave 3: producers publish to OpenVibe.Events (their own source only, enforced by Events).
@@ -76,6 +76,17 @@ const runTokens = require('../server/identity/run-tokens');
@@ -76,6 +76,17 @@ const runTokens = require('../server/identity/run-tokens');
const codesGrants = await principals.grantsFor(db, 'codes', 'openvibe.ai');
const codesGrants = await principals.grantsFor(db, 'codes', 'openvibe.ai');
assert.deepStrictEqual(codesGrants.map((g) => g.capability).sort(), ['ai.credential.manage', 'ai.run.create']);
assert.deepStrictEqual(codesGrants.map((g) => g.capability).sort(), ['ai.credential.manage', 'ai.run.create']);
+ // ── The grants Actor holds for OpenVibe.AI (plan L55 C3) ──
+ // A person chooses the model their tasks run on, with their own key (sealed in AI) or OpenVibe's. Actor runs
+ // the call through AI's OpenAI-compatible endpoint (workflow ai.complete) and nothing else, and it never
+ // holds a key of its own: no consent grant, so it may not store a person's key in AI.
+ assert.ok(has('actor', 'ai.run.create', 'openvibe.ai'), 'Actor runs AI\'s OpenAI-compatible endpoint');
+ assert.ok(!has('actor', 'ai.credential.manage', 'openvibe.ai'), 'Actor never stores a person\'s key in AI');
+ const actorGrant = principals.DEFAULT_GRANTS.find((g) => g[0] === 'actor' && g[1] === 'ai.run.create' && g[2] === 'openvibe.ai');
+ assert.deepStrictEqual(actorGrant[3], ['ai.complete'], 'AI\'s OpenAI-compatible workflow only');
+ const actorGrants = await principals.grantsFor(db, 'actor', 'openvibe.ai');
+ assert.deepStrictEqual(actorGrants.map((g) => g.capability).sort(), ['ai.run.create']);
+
// ── A run token: the person it acts for, and how long it lives ──
// ── A run token: the person it acts for, and how long it lives ──
let t = await runToken(OWNER);
let t = await runToken(OWNER);
assert.strictEqual(t.status, 200, JSON.stringify(t.body));
assert.strictEqual(t.status, 200, JSON.stringify(t.body));