openvibers/openvibe.sdk Commits

Dock v2: Vibe conversations and Actor skill methods (#159)

OP OpenVibers committed 4 days ago · 8ca81992d7 · 13 files, +31 −10

* Add Vibe conversation and Actor skill run SDK methods

* Merge remote-tracking branch 'origin/main' into auto/dock-v2-sdk

* 0.110.0: the Dock v2's Vibe conversations and skill runs

ov.chat.vibe.conversations() reads a person's Vibe conversations with the last line,
when Vibe last answered and the site each chat began on. ov.actor.skills() reads
Actor's public catalogue and ov.actor.run() starts one of its skills as a normal
task for that person.

* 0.114.0

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 6ce558a..90a1db2 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -1,5 +1,9 @@
# Changelog
++## 0.114.0 (2026-11-09)
++
++- Dock v2: Vibe conversations (ov.chat.vibe.conversations) and Actor skill runs (ov.actor.skills.run)
++
## 0.113.0 — 2026-11-09
- Add the Feed and status posts to `openvibe-sdk/community`: `feed({ kind, site, q, following, cursor, limit })` reads
ddiff --git a/README.md b/README.md
iindex 71f835f..9d49f6b 100644
---- a/README.md
++++ b/README.md
@@@ -2,7 +2,7 @@
> Supported browser and server clients for the OpenVibe platform.
--**Status:** alpha, v0.113.0 (the five 1.0 core entries — `core`, `auth`, `events`, `db` and `media` — are frozen: their exported names change only with a major release; roadmap Wave 2; developer apps from Wave 20; Media objects v2 from Wave 4; the Tools platform API; Search, Chat, AI, Vision and consent clients; sign-in with recovery and a client health check). Every release is tagged (see [CHANGELOG.md](CHANGELOG.md)). Tested against local stub servers and the built-in mock platform only, never against the live platform. Services pin release tags; each repository's `STATUS.json` names the one it uses.
++**Status:** alpha, v0.114.0 (the five 1.0 core entries — `core`, `auth`, `events`, `db` and `media` — are frozen: their exported names change only with a major release; roadmap Wave 2; developer apps from Wave 20; Media objects v2 from Wave 4; the Tools platform API; Search, Chat, AI, Vision and consent clients; sign-in with recovery and a client health check). Every release is tagged (see [CHANGELOG.md](CHANGELOG.md)). Tested against local stub servers and the built-in mock platform only, never against the live platform. Services pin release tags; each repository's `STATUS.json` names the one it uses.
**Plan:** OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §3.2; roadmap §30.
**License:** MIT ([LICENSE](LICENSE)). This package is a library that apps outside the network embed, so it uses MIT. The OpenVibe services themselves stay AGPL-3.0.
@@@ -11,7 +11,7 @@
After v0.97.1 is tagged:
```bash
--npm install https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.113.0
++npm install https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.114.0
```
It has no runtime dependencies. It needs Node ≥ 20, or any browser with `fetch`, Web Crypto and `TextDecoder`. There is no build step. The package is CommonJS with ESM entry points (`import` works). Each subpath has its own `.d.ts`. For a page with no bundler, `browser/openvibe-sdk.mjs` is one self-contained ES module of the browser-safe subpaths (see [Browser without a bundler](#browser-without-a-bundler)). It does not depend on `openvibe-contracts` at runtime: it copies the contract types it uses (from the temporary Contracts sharing branch, a devDependency the tests check them against).
ddiff --git a/STATUS.json b/STATUS.json
iindex 07925ab..17e6022 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -3,7 +3,7 @@
"stage": "alpha",
"code": true,
"wave": "W2",
-- "version": "0.113.0",
++ "version": "0.114.0",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §3.2; roadmap §30",
"deployed": false,
"deployment": "none: a package released as git tags; services and apps pin a tag",
ddiff --git a/browser/openvibe-sdk.mjs b/browser/openvibe-sdk.mjs
iindex afe4e64..387c0b0 100644
---- a/browser/openvibe-sdk.mjs
++++ b/browser/openvibe-sdk.mjs
@@@ -1,4 +1,4 @@
--// Generated by scripts/browser-bundle.js from browser.js (openvibe-sdk 0.113.0, MIT). Do not edit.
++// Generated by scripts/browser-bundle.js from browser.js (openvibe-sdk 0.114.0, MIT). Do not edit.
// One self-contained ES module: core at the top level plus the browser-safe subpaths as namespaces
// (auth, locker, registry, trust, modules, realtime, media, cam, community, homeFeed, bot, search, bench, meme, vision, themes, redact, chat, ai, credentials, jobs, tools, projects, geo). No server-only code: no service tokens, JWT verification, events, identity.
/* eslint-disable */
@@@ -52,7 +52,7 @@ const { paginate, offsetPager } = __load(7);
const semver = __load(6);
/** The package version (test/version.test.js keeps it equal to package.json). */
--const SDK_VERSION = '0.113.0';
++const SDK_VERSION = '0.114.0';
module.exports = {
createClient,
@@@ -4630,6 +4630,8 @@ function createChatClient(client, defaults = {}) {
vibe: {
/** { chats, allowance } */
list: (o = {}) => call({ path: '/api/chat/vibe' }, o),
++ /** The same conversations in Chat and the Dock, with last line, answer time and originating site. */
++ conversations: (o = {}) => call({ path: '/api/vibe/conversations' }, o),
/** A new chat; Vibe answers in its room (rooms.messages, or the room socket). → { room, message } */
start: (message, o = {}) => call({ method: 'POST', path: '/api/chat/vibe', json: { message }, ...once }, o),
rename: (slug, name, o = {}) => call({ method: 'PATCH', path: `/api/chat/vibe/${enc(slug)}`, json: { name }, ...once }, o),
ddiff --git a/package-lock.json b/package-lock.json
iindex 9f33921..6c748e4 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-sdk",
-- "version": "0.113.0",
++ "version": "0.114.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-sdk",
-- "version": "0.113.0",
++ "version": "0.114.0",
"license": "MIT",
"bin": {
"openvibe-download": "bin/openvibe-download.js"
ddiff --git a/package.json b/package.json
iindex fdb2366..d00dfb0 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-sdk",
-- "version": "0.113.0",
++ "version": "0.114.0",
"description": "Supported browser and server clients for the OpenVibe platform: auth (incl. developer apps), registry discovery, identity, user modules, events, realtime, media, community, Bot robots, search, chat, AI runs, Tools (registry, run API and jobs), VIP memberships and developer projects, the async PostgreSQL data layer (db) and the Valkey cache, limits store, queue and pub/sub, with deadlines, safe retries, problem errors and trace propagation.",
"main": "index.js",
"types": "types/index.d.ts",
ddiff --git a/src/actor.js b/src/actor.js
iindex 678c0f4..069635d 100644
---- a/src/actor.js
++++ b/src/actor.js
@@@ -64,6 +64,10 @@ function createActorClient(client, { baseUrl } = {}) {
}
return {
++ /** The public catalogue of skills a person can run. */
++ skills: ({ signal } = {}) => call({ path: '/api/v1/skills', signal }),
++ /** Start one catalogue skill as a normal Actor task, for this person. */
++ run: (skill, input = {}, { choice = { mode: 'balanced' }, signal } = {}) => once({ method: 'POST', path: '/api/v1/runs', json: { skill, input, choice }, signal }),
agents: {
/** The agent systems a task can go to, with what they are good at and cost (public). */
catalog: ({ signal } = {}) => call({ path: '/api/v1/agent-systems', signal }),
ddiff --git a/src/chat.js b/src/chat.js
iindex 8de734d..dd831ff 100644
---- a/src/chat.js
++++ b/src/chat.js
@@@ -90,6 +90,8 @@ function createChatClient(client, defaults = {}) {
vibe: {
/** { chats, allowance } */
list: (o = {}) => call({ path: '/api/chat/vibe' }, o),
++ /** The same conversations in Chat and the Dock, with last line, answer time and originating site. */
++ conversations: (o = {}) => call({ path: '/api/vibe/conversations' }, o),
/** A new chat; Vibe answers in its room (rooms.messages, or the room socket). → { room, message } */
start: (message, o = {}) => call({ method: 'POST', path: '/api/chat/vibe', json: { message }, ...once }, o),
rename: (slug, name, o = {}) => call({ method: 'PATCH', path: `/api/chat/vibe/${enc(slug)}`, json: { name }, ...once }, o),
ddiff --git a/src/core/index.js b/src/core/index.js
iindex 1ac893e..6e2ecff 100644
---- a/src/core/index.js
++++ b/src/core/index.js
@@@ -8,7 +8,7 @@ const { paginate, offsetPager } = require('./paginate');
const semver = require('./semver');
/** The package version (test/version.test.js keeps it equal to package.json). */
--const SDK_VERSION = '0.113.0';
++const SDK_VERSION = '0.114.0';
module.exports = {
createClient,
ddiff --git a/test/actor.test.js b/test/actor.test.js
iindex b82c2d1..880208a 100644
---- a/test/actor.test.js
++++ b/test/actor.test.js
@@@ -22,6 +22,8 @@ const agent = (over = {}) => ({
seen.push({ method: req.method, url: req.url, auth: req.headers.authorization || null, body });
const json = (status, obj) => { res.writeHead(status, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(obj)); };
if (req.url === '/api/v1/agent-systems') return json(200, { agents: [{ id: 'openvibe-runtime' }] });
++ if (req.url === '/api/v1/skills') return json(200, { listings: [{ id: 'skl_example' }] });
++ if (req.url === '/api/v1/runs' && req.method === 'POST') return json(201, { run: { id: 'tsk_example' } });
if (req.url === '/api/v1/agents/presets') return json(200, { presets: [{ id: 'researcher', name: 'Researcher' }] });
if (req.url === '/api/v1/agents' && req.method === 'GET') return json(200, { agents: [agent()] });
if (req.url === '/api/v1/agents' && req.method === 'POST') return json(201, agent({ name: 'Researcher' }));
@@@ -46,11 +48,13 @@ const agent = (over = {}) => ({
await actor.contacts.remove(AGENT);
assert.deepStrictEqual(await actor.agents.catalog(), { agents: [{ id: 'openvibe-runtime' }] });
assert.deepStrictEqual(await actor.agents.list(), { agents: [{ id: 'openvibe-runtime' }] });
++ assert.deepStrictEqual(await actor.skills(), { listings: [{ id: 'skl_example' }] });
++ assert.deepStrictEqual(await actor.run('skl_example'), { run: { id: 'tsk_example' } });
assert.deepStrictEqual(seen.map((s) => `${s.method} ${s.url}`), [
'GET /api/v1/agents', 'GET /api/v1/agents/presets', `GET /api/v1/agents/${AGENT}`,
'POST /api/v1/agents', `PATCH /api/v1/agents/${AGENT}`, `PUT /api/v1/agents/${AGENT}/level`,
-- `DELETE /api/v1/agents/${AGENT}`, 'GET /api/v1/agent-systems', 'GET /api/v1/agent-systems',
++ `DELETE /api/v1/agents/${AGENT}`, 'GET /api/v1/agent-systems', 'GET /api/v1/agent-systems', 'GET /api/v1/skills', 'POST /api/v1/runs',
]);
assert.ok(seen.every((s) => s.auth === 'Bearer person-token'), 'the person token rides every call');
assert.strictEqual(JSON.parse(seen[3].body).preset, 'researcher');
ddiff --git a/test/chat.test.js b/test/chat.test.js
iindex 479d054..6aacdcd 100644
---- a/test/chat.test.js
++++ b/test/chat.test.js
@@@ -77,6 +77,7 @@ run([
await chat.feeds.view('fed_Z', { limit: 5 });
await chat.feeds.onLive({ slot: [77, 78] });
await chat.vibe.start('hello');
++ await chat.vibe.conversations();
await chat.inboxes.saveReply('inb_X', 'refund', 'Five days.');
await chat.inboxes.addWebhook('inb_X', 'https://hooks.slack.com/services/x');
await chat.inboxes.assign('inb_X', 'cnv_Y', 7);
@@@ -93,6 +94,7 @@ run([
['GET', '/api/chat/feeds/fed_Z?limit=5', null],
['GET', '/api/chat/feeds?slot=77%2C78', null],
['POST', '/api/chat/vibe', { message: 'hello' }],
++ ['GET', '/api/vibe/conversations', null],
['POST', '/api/chat/inboxes/inb_X/replies', { shortcut: 'refund', body: 'Five days.' }],
['POST', '/api/chat/inboxes/inb_X/webhooks', { url: 'https://hooks.slack.com/services/x' }],
['PATCH', '/api/chat/inboxes/inb_X/conversations/cnv_Y', { assignee_id: 7 }],
ddiff --git a/types/actor.d.ts b/types/actor.d.ts
iindex 3600fbc..412e636 100644
---- a/types/actor.d.ts
++++ b/types/actor.d.ts
@@@ -19,6 +19,8 @@ export interface TaskRequest {
}
export interface TaskEvent { id: number | null; event: string; data: any }
export interface ActorClient {
++ skills(opts?: Opts): Promise<{ listings: ActorJson[] }>;
++ run(skill: string, input?: Record<string, unknown>, opts?: Opts & { choice?: { mode?: ActorMode } }): Promise<{ run: ActorJson }>;
agents: { catalog(opts?: Opts): Promise<{ agents: ActorJson[] }>; list(opts?: Opts): Promise<{ agents: ActorJson[] }> };
contacts: {
list(opts?: Opts): Promise<{ agents: ActorAgent[] }>;
ddiff --git a/types/chat.d.ts b/types/chat.d.ts
iindex 0892b06..139dbc2 100644
---- a/types/chat.d.ts
++++ b/types/chat.d.ts
@@@ -42,6 +42,7 @@ export interface ChatClient {
exportMine(opts?: Opts): Promise<{ username: string; exported_at: string; total: number; truncated: boolean; messages: Json[] }>;
vibe: {
list(opts?: Opts): Promise<{ chats: Json[]; allowance?: Json }>;
++ conversations(opts?: Opts): Promise<{ conversations: Array<{ slug: string; name: string; last_message_at: string; created_at: string; last_line: string | null; last_answer_at: string | null; site: string | null }>; count: number }>;
start(message: string, opts?: Opts): Promise<{ room: Json; message: Json }>;
rename(slug: string, name: string, opts?: Opts): Promise<Json>;
remove(slug: string, opts?: Opts): Promise<Json>;