openvibers/openvibe.shared Commits
Shared 4.136.0: the Dock's step and Now read the one Computer (#324)
Changes
@@@ -1,5 +1,14 @@# Changelog++## 4.136.0 — 2026-11-07++++- **One Computer in the Dock** (plan 2026-11-07: "Surf, Computer and Actor are one experience"): a timeline step++ that works on a Computer shows its machine and cost hint (`computer.machine`, `computer.cost_hint`) — the same++ object the task record and Surf's card carry, never a copy. The Now card offers **Take over** (and **Give it++ back** while you drive) on a step that names its task (`ref`) and its Computer: one call to OpenVibe.Actor's++ `POST /api/v1/tasks/{id}/takeover` or `…/giveback`, the same hand-off the agent's own `hand_to_person` uses.++ No new layout and no new CSS: the line is an existing meta row and the button an existing Dock button.++## 4.135.0 — 2026-11-07- **Ask your agent, from any page** (plan Z3; the owner, 11-07: "have your agent with you wherever you go"):@@@ -2,7 +2,7 @@> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.--**Status:** alpha, v4.135.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed++**Status:** alpha, v4.136.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployedconsumer installs a tagged release and none keeps a vendored copy([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` namesthe tag it pins.@@@ -4,7 +4,7 @@"code": true,"wave": "W2","package": "openvibe-shared",-- "version": "4.135.0",++ "version": "4.136.0","plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)","deployed": false,"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",@@@ -174,7 +174,27 @@if (now.placement && (now.placement.where || now.placement.why)) {card.appendChild(el(doc, 'div', 'ovdt-meta', [now.placement.where, now.placement.why].filter(Boolean).join(': ')));}++ // The Computer this step works on (plan 2026-11-07): the same machine and cost the task record carries.++ if (now.computer) card.appendChild(el(doc, 'div', 'ovdt-meta', [now.computer.machine, now.computer.cost_hint].filter(Boolean).join(' · ')));const buttons = el(doc, 'div', 'ovdt-buttons');++ // Take over / Give it back: one API on the task at Actor, the same hand-off the agent's own++ // hand_to_person tool uses. Only a step that names its task (ref) and its Computer offers it.++ if (now.computer && now.ref) {++ const driving = now.computer.driving === true || /^You took over/.test(String(now.title || ''));++ const take = el(doc, 'button', 'ovdt-btn is-primary', driving ? 'Give it back' : 'Take over');++ take.type = 'button';++ take.addEventListener('click', async () => {++ take.disabled = true;++ const auth = await bearer();++ try {++ await api('/api/v1/tasks/' + encodeURIComponent(now.ref) + (driving ? '/giveback' : '/takeover'), {++ method: 'POST', headers: { 'Content-Type': 'application/json', ...(auth ? { Authorization: auth } : {}) }, body: '{}',++ });++ } catch (err) { /* the panel refreshes either way */ }++ refresh();++ });++ buttons.appendChild(take);++ }const stop = el(doc, 'button', 'ovdt-btn is-stop', 'Stop');stop.type = 'button';stop.addEventListener('click', async () => {@@@ -236,6 +256,7 @@else if (entry.placement && (entry.placement.where || entry.placement.why)) {card.appendChild(el(doc, 'div', 'ovdt-meta', [entry.placement.where, entry.placement.why].filter(Boolean).join(': ')));}++ if (entry.computer) card.appendChild(el(doc, 'div', 'ovdt-meta', [entry.computer.machine, entry.computer.cost_hint].filter(Boolean).join(' · ')));if (entry.reason) card.appendChild(el(doc, 'div', 'ovdt-meta', entry.reason));if (entry.cost_usd !== null && entry.cost_usd !== undefined) card.appendChild(el(doc, 'div', 'ovdt-meta', '$' + Number(entry.cost_usd).toFixed(2)));if (entry.link) {@@@ -1,6 +1,6 @@{"name": "openvibe-shared",-- "version": "4.135.0",++ "version": "4.136.0","description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site","main": "index.js","bin": {@@@ -26,6 +26,10 @@ assert.ok(panel.includes('placement'), 'the entry says where it ran and why');assert.ok(panel.includes('transition'), 'a mode change is a line of its own');assert.ok(panel.includes('cost_usd'), 'the cost is shown when there was one');assert.ok(panel.includes('entry.link'), 'the entry links to the step\'s own page');++// One Computer (plan 2026-11-07): the step reads the same fields the task record carries, and Now can hand over.++assert.ok(panel.includes('entry.computer'), 'a step names its Computer');++assert.ok(panel.includes('now.computer'), 'Now names the Computer');++assert.ok(panel.includes('/takeover') && panel.includes('/giveback'), 'the Dock calls the one take-over API');// The folds and the filters.assert.ok(panel.includes('Yesterday ('), 'earlier days fold with a count');
@@ -1,5 +1,14 @@
@@ -1,5 +1,14 @@
# Changelog
# Changelog
+## 4.136.0 — 2026-11-07
+
+- **One Computer in the Dock** (plan 2026-11-07: "Surf, Computer and Actor are one experience"): a timeline step
+ that works on a Computer shows its machine and cost hint (`computer.machine`, `computer.cost_hint`) — the same
+ object the task record and Surf's card carry, never a copy. The Now card offers **Take over** (and **Give it
+ back** while you drive) on a step that names its task (`ref`) and its Computer: one call to OpenVibe.Actor's
+ `POST /api/v1/tasks/{id}/takeover` or `…/giveback`, the same hand-off the agent's own `hand_to_person` uses.
+ No new layout and no new CSS: the line is an existing meta row and the button an existing Dock button.
+
## 4.135.0 — 2026-11-07
## 4.135.0 — 2026-11-07
- **Ask your agent, from any page** (plan Z3; the owner, 11-07: "have your agent with you wherever you go"):
- **Ask your agent, from any page** (plan Z3; the owner, 11-07: "have your agent with you wherever you go"):
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.
> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.
-**Status:** alpha, v4.135.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
+**Status:** alpha, v4.136.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
consumer installs a tagged release and none keeps a vendored copy
consumer installs a tagged release and none keeps a vendored copy
([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` names
([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` names
the tag it pins.
the tag it pins.
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
"code": true,
"code": true,
"wave": "W2",
"wave": "W2",
"package": "openvibe-shared",
"package": "openvibe-shared",
- "version": "4.135.0",
+ "version": "4.136.0",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"deployed": false,
"deployed": false,
"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",
"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",
@@ -174,7 +174,27 @@
@@ -174,7 +174,27 @@
if (now.placement && (now.placement.where || now.placement.why)) {
if (now.placement && (now.placement.where || now.placement.why)) {
card.appendChild(el(doc, 'div', 'ovdt-meta', [now.placement.where, now.placement.why].filter(Boolean).join(': ')));
card.appendChild(el(doc, 'div', 'ovdt-meta', [now.placement.where, now.placement.why].filter(Boolean).join(': ')));
}
}
+ // The Computer this step works on (plan 2026-11-07): the same machine and cost the task record carries.
+ if (now.computer) card.appendChild(el(doc, 'div', 'ovdt-meta', [now.computer.machine, now.computer.cost_hint].filter(Boolean).join(' · ')));
const buttons = el(doc, 'div', 'ovdt-buttons');
const buttons = el(doc, 'div', 'ovdt-buttons');
+ // Take over / Give it back: one API on the task at Actor, the same hand-off the agent's own
+ // hand_to_person tool uses. Only a step that names its task (ref) and its Computer offers it.
+ if (now.computer && now.ref) {
+ const driving = now.computer.driving === true || /^You took over/.test(String(now.title || ''));
+ const take = el(doc, 'button', 'ovdt-btn is-primary', driving ? 'Give it back' : 'Take over');
+ take.type = 'button';
+ take.addEventListener('click', async () => {
+ take.disabled = true;
+ const auth = await bearer();
+ try {
+ await api('/api/v1/tasks/' + encodeURIComponent(now.ref) + (driving ? '/giveback' : '/takeover'), {
+ method: 'POST', headers: { 'Content-Type': 'application/json', ...(auth ? { Authorization: auth } : {}) }, body: '{}',
+ });
+ } catch (err) { /* the panel refreshes either way */ }
+ refresh();
+ });
+ buttons.appendChild(take);
+ }
const stop = el(doc, 'button', 'ovdt-btn is-stop', 'Stop');
const stop = el(doc, 'button', 'ovdt-btn is-stop', 'Stop');
stop.type = 'button';
stop.type = 'button';
stop.addEventListener('click', async () => {
stop.addEventListener('click', async () => {
@@ -236,6 +256,7 @@
@@ -236,6 +256,7 @@
else if (entry.placement && (entry.placement.where || entry.placement.why)) {
else if (entry.placement && (entry.placement.where || entry.placement.why)) {
card.appendChild(el(doc, 'div', 'ovdt-meta', [entry.placement.where, entry.placement.why].filter(Boolean).join(': ')));
card.appendChild(el(doc, 'div', 'ovdt-meta', [entry.placement.where, entry.placement.why].filter(Boolean).join(': ')));
}
}
+ if (entry.computer) card.appendChild(el(doc, 'div', 'ovdt-meta', [entry.computer.machine, entry.computer.cost_hint].filter(Boolean).join(' · ')));
if (entry.reason) card.appendChild(el(doc, 'div', 'ovdt-meta', entry.reason));
if (entry.reason) card.appendChild(el(doc, 'div', 'ovdt-meta', entry.reason));
if (entry.cost_usd !== null && entry.cost_usd !== undefined) card.appendChild(el(doc, 'div', 'ovdt-meta', '$' + Number(entry.cost_usd).toFixed(2)));
if (entry.cost_usd !== null && entry.cost_usd !== undefined) card.appendChild(el(doc, 'div', 'ovdt-meta', '$' + Number(entry.cost_usd).toFixed(2)));
if (entry.link) {
if (entry.link) {
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-shared",
"name": "openvibe-shared",
- "version": "4.135.0",
+ "version": "4.136.0",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"main": "index.js",
"bin": {
"bin": {
@@ -26,6 +26,10 @@ assert.ok(panel.includes('placement'), 'the entry says where it ran and why');
@@ -26,6 +26,10 @@ assert.ok(panel.includes('placement'), 'the entry says where it ran and why');
assert.ok(panel.includes('transition'), 'a mode change is a line of its own');
assert.ok(panel.includes('transition'), 'a mode change is a line of its own');
assert.ok(panel.includes('cost_usd'), 'the cost is shown when there was one');
assert.ok(panel.includes('cost_usd'), 'the cost is shown when there was one');
assert.ok(panel.includes('entry.link'), 'the entry links to the step\'s own page');
assert.ok(panel.includes('entry.link'), 'the entry links to the step\'s own page');
+// One Computer (plan 2026-11-07): the step reads the same fields the task record carries, and Now can hand over.
+assert.ok(panel.includes('entry.computer'), 'a step names its Computer');
+assert.ok(panel.includes('now.computer'), 'Now names the Computer');
+assert.ok(panel.includes('/takeover') && panel.includes('/giveback'), 'the Dock calls the one take-over API');
// The folds and the filters.
// The folds and the filters.
assert.ok(panel.includes('Yesterday ('), 'earlier days fold with a count');
assert.ok(panel.includes('Yesterday ('), 'earlier days fold with a count');