openzoo 0.50.52 → 0.50.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cursorbackend.js +18 -7
- package/lib/grokbotAccount.js +110 -13
- package/lib/ozSpendChip.js +66 -24
- package/package.json +1 -1
package/lib/cursorbackend.js
CHANGED
|
@@ -40,6 +40,7 @@ import { encodeAvailableModels, encodeForMethod, encodeEnsureSandBox, encodeGetG
|
|
|
40
40
|
import {
|
|
41
41
|
accountPodPath, accountAgentsPath, rosterForAccount, rosterForEvent,
|
|
42
42
|
readHouseRoster, houseAgentsPath, shapeAgent, agentBrief, briefFromName,
|
|
43
|
+
preferNamedAgent, looksLikeAgentId,
|
|
43
44
|
readWakeups, writeWakeups, shapeWakeup, parseWakeupEvery, wantsWakeupCron,
|
|
44
45
|
DEFAULT_WAKEUP_PROMPT, addDeletedIds, filterDeleted,
|
|
45
46
|
} from './grokbotAccount.js';
|
|
@@ -365,7 +366,12 @@ function useHouseRoster() {
|
|
|
365
366
|
function loadAgents() {
|
|
366
367
|
const house = filterDeleted(readHouseRoster(HOME, activeAccountId) || [], HOME);
|
|
367
368
|
const shaped = house.map(shapeAgent);
|
|
368
|
-
const dirty = shaped.some((a, i) =>
|
|
369
|
+
const dirty = shaped.some((a, i) => {
|
|
370
|
+
const prev = house[i] || {};
|
|
371
|
+
return (a.brief && a.brief !== String(prev.brief || prev.description || ''))
|
|
372
|
+
|| (a.name && a.name !== String(prev.name || prev.title || ''))
|
|
373
|
+
|| looksLikeAgentId(prev.name, prev.id);
|
|
374
|
+
});
|
|
369
375
|
if (dirty && shaped.length) {
|
|
370
376
|
writeJsonFile(houseAgentsPath(HOME), shaped);
|
|
371
377
|
if (activeAccountId) {
|
|
@@ -944,7 +950,7 @@ function bumpAgent(id, { preview = '', notify = true } = {}) {
|
|
|
944
950
|
agentActivity.set(id, a);
|
|
945
951
|
const list = cachedAgentList() || [];
|
|
946
952
|
const idx = list.findIndex((x) => x.id === id);
|
|
947
|
-
const base = idx >= 0 ? list[idx] : { id, name:
|
|
953
|
+
const base = idx >= 0 ? list[idx] : { id, name: 'chat' };
|
|
948
954
|
const agent = shapeAgent(stampActivity({ ...base, updatedAt: now }));
|
|
949
955
|
if (idx >= 0) list[idx] = agent;
|
|
950
956
|
else list.unshift(agent);
|
|
@@ -1533,12 +1539,17 @@ async function runGroupQueue({ agentId, humanPrompt, parsed, nonce, log }) {
|
|
|
1533
1539
|
}
|
|
1534
1540
|
function mergeAgentLists(remote) {
|
|
1535
1541
|
const local = cachedAgentList() || [];
|
|
1536
|
-
const seen = new
|
|
1542
|
+
const seen = new Map();
|
|
1537
1543
|
const out = [];
|
|
1538
1544
|
for (const a of [...local, ...(Array.isArray(remote) ? remote : [])]) {
|
|
1539
|
-
if (!a?.id
|
|
1540
|
-
seen.
|
|
1541
|
-
|
|
1545
|
+
if (!a?.id) continue;
|
|
1546
|
+
const idx = seen.get(a.id);
|
|
1547
|
+
if (idx == null) {
|
|
1548
|
+
seen.set(a.id, out.length);
|
|
1549
|
+
out.push(stampActivity(a));
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1552
|
+
out[idx] = stampActivity(preferNamedAgent(out[idx], a));
|
|
1542
1553
|
}
|
|
1543
1554
|
return sortAgentsByActivity(filterDeleted(out, HOME));
|
|
1544
1555
|
}
|
|
@@ -3113,7 +3124,7 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
|
|
|
3113
3124
|
setHostSettings: { ok: true },
|
|
3114
3125
|
setBoxSecrets: { ok: true },
|
|
3115
3126
|
listAgents: rosterForEvent(cachedAgentList()
|
|
3116
|
-
|| [...new Set([...transcripts.keys(), ...tailedAgents])].map((id) => ({ id, name:
|
|
3127
|
+
|| [...new Set([...transcripts.keys(), ...tailedAgents])].map((id) => ({ id, name: 'chat', status: 'ready' })), agentActivity),
|
|
3117
3128
|
countAgents: (cachedAgentList() || [...new Set([...transcripts.keys(), ...tailedAgents])]).length,
|
|
3118
3129
|
searchAgents: rosterForEvent(cachedAgentList() || [], agentActivity),
|
|
3119
3130
|
getAgentTranscriptTail: { tail: '', lines: [], dropped: false, ok: true },
|
package/lib/grokbotAccount.js
CHANGED
|
@@ -41,16 +41,21 @@ function readJsonFile(p) {
|
|
|
41
41
|
try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; }
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/** First-seen id wins.
|
|
44
|
+
/** First-seen id wins. A later pile can only *upgrade* a UUID name/stub brief. */
|
|
45
45
|
export function mergeAgentRecords(piles) {
|
|
46
|
-
const seen = new
|
|
46
|
+
const seen = new Map();
|
|
47
47
|
const out = [];
|
|
48
48
|
for (const pile of piles) {
|
|
49
49
|
if (!Array.isArray(pile)) continue;
|
|
50
50
|
for (const a of pile) {
|
|
51
|
-
if (!a?.id
|
|
52
|
-
seen.
|
|
53
|
-
|
|
51
|
+
if (!a?.id) continue;
|
|
52
|
+
const idx = seen.get(a.id);
|
|
53
|
+
if (idx == null) {
|
|
54
|
+
seen.set(a.id, out.length);
|
|
55
|
+
out.push(a);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
out[idx] = preferNamedAgent(out[idx], a);
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
return out;
|
|
@@ -111,10 +116,94 @@ function activityTs(agent, activity) {
|
|
|
111
116
|
* nulls, then clears the whole persisted tray. That is how a group vanished
|
|
112
117
|
* after the first send: bumpAgent wrote a partial row, restore returned null.
|
|
113
118
|
*/
|
|
119
|
+
const AGENT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
120
|
+
|
|
121
|
+
/** bumpAgent used `{id, name:id}` and 1340 listAgents often echoes that — sidebar then paints UUIDs. */
|
|
122
|
+
export function looksLikeAgentId(s, id) {
|
|
123
|
+
const n = String(s || '').trim();
|
|
124
|
+
if (!n) return true;
|
|
125
|
+
if (id && n === String(id)) return true;
|
|
126
|
+
return AGENT_UUID_RE.test(n);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function isStubBrief(brief, id) {
|
|
130
|
+
const b = String(brief || '').trim();
|
|
131
|
+
if (!b) return true;
|
|
132
|
+
if (id && b.startsWith(`You are ${id}. Your job is ${id}`)) return true;
|
|
133
|
+
return /^You are [0-9a-f-]{36}\. Your job is [0-9a-f-]{36}/i.test(b);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Pull a human name out of a standing brief: "You are 6 · Content Studio…" / "Job: Product Simplification". */
|
|
137
|
+
export function nameFromBrief(brief, id) {
|
|
138
|
+
const b = String(brief || '').replace(/^\[brief\]\s*/i, '').trim();
|
|
139
|
+
if (!b) return '';
|
|
140
|
+
const job = b.match(/\bJob:\s*([^\n.]+)/i);
|
|
141
|
+
if (job) {
|
|
142
|
+
const n = job[1].trim();
|
|
143
|
+
if (n && !looksLikeAgentId(n, id)) return n.slice(0, 80);
|
|
144
|
+
}
|
|
145
|
+
const numbered = b.match(/^You are\s+(\d+\s*[·.•.\-—–]+\s*[^\n.]+)/i);
|
|
146
|
+
if (numbered) {
|
|
147
|
+
const n = numbered[1].replace(/\s+for\s+.*$/i, '').trim();
|
|
148
|
+
if (n && !looksLikeAgentId(n, id)) return n.slice(0, 80);
|
|
149
|
+
}
|
|
150
|
+
const you = b.match(/^You are\s+(.+?)(?:\.\s|$)/i);
|
|
151
|
+
if (you) {
|
|
152
|
+
const n = you[1].replace(/\s+for\s+Stacc(?:'s)?(?:\s+LLC)?$/i, '').trim();
|
|
153
|
+
if (n && !looksLikeAgentId(n, id)) return n.slice(0, 80);
|
|
154
|
+
}
|
|
155
|
+
return '';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function nameQuality(a) {
|
|
159
|
+
const n = String(a?.name || a?.title || '').trim();
|
|
160
|
+
if (!n || looksLikeAgentId(n, a?.id)) return 0;
|
|
161
|
+
if (/^(chat|group)$/i.test(n)) return 1;
|
|
162
|
+
if (/^new bot$/i.test(n)) return 2;
|
|
163
|
+
return 3;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Keep the record that still has a human name / real brief. */
|
|
167
|
+
export function preferNamedAgent(keep, incoming) {
|
|
168
|
+
if (!keep) return incoming;
|
|
169
|
+
if (!incoming) return keep;
|
|
170
|
+
const kq = nameQuality(keep);
|
|
171
|
+
const iq = nameQuality(incoming);
|
|
172
|
+
const keepBrief = String(keep.brief || keep.instructions || '').trim();
|
|
173
|
+
const inBrief = String(incoming.brief || incoming.instructions || '').trim();
|
|
174
|
+
const keepStub = isStubBrief(keepBrief, keep.id);
|
|
175
|
+
const inStub = isStubBrief(inBrief, incoming.id);
|
|
176
|
+
if (iq <= kq && !(keepStub && !inStub)) return keep;
|
|
177
|
+
const nameSrc = iq > kq ? incoming : keep;
|
|
178
|
+
const briefSrc = (!inStub && keepStub) ? incoming : keep;
|
|
179
|
+
return {
|
|
180
|
+
...keep,
|
|
181
|
+
...incoming,
|
|
182
|
+
name: nameSrc.name || nameSrc.title,
|
|
183
|
+
title: nameSrc.title || nameSrc.name,
|
|
184
|
+
brief: briefSrc.brief || briefSrc.instructions || keepBrief || inBrief,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function displayName(raw = {}) {
|
|
189
|
+
const a = raw && typeof raw === 'object' ? raw : {};
|
|
190
|
+
const id = String(a.id || '');
|
|
191
|
+
const isGroup = a.isGroup === true
|
|
192
|
+
|| (Array.isArray(a.memberIds) && a.memberIds.length > 0)
|
|
193
|
+
|| (Array.isArray(a.memberAgentIds) && a.memberAgentIds.length > 0);
|
|
194
|
+
for (const cand of [a.name, a.title]) {
|
|
195
|
+
const n = String(cand || '').trim();
|
|
196
|
+
if (n && !looksLikeAgentId(n, id)) return n.slice(0, 80);
|
|
197
|
+
}
|
|
198
|
+
const fromBrief = nameFromBrief(a.brief || a.instructions || a.description, id);
|
|
199
|
+
if (fromBrief) return fromBrief;
|
|
200
|
+
return isGroup ? 'group' : 'chat';
|
|
201
|
+
}
|
|
202
|
+
|
|
114
203
|
/** Standing job from a sidebar name like "6 · Content Studio" or "Bot 1 — Marketing (X)". */
|
|
115
204
|
export function briefFromName(name) {
|
|
116
205
|
const n = String(name || '').trim();
|
|
117
|
-
if (!n || /^(new bot|chat|group)$/i.test(n)) return '';
|
|
206
|
+
if (!n || /^(new bot|chat|group)$/i.test(n) || looksLikeAgentId(n)) return '';
|
|
118
207
|
const role = n.replace(/^\d+\s*[·.•.\-—–]+\s*/, '').trim() || n;
|
|
119
208
|
if (role.length < 2) return '';
|
|
120
209
|
return `You are ${n}. Your job is ${role}. Do that job. Do not ask the human to re-brief you. Coordinate with list_agents and message_agent.`;
|
|
@@ -123,13 +212,21 @@ export function briefFromName(name) {
|
|
|
123
212
|
/** Standing job text. shapeAgent used to drop this, so every restart was amnesia. */
|
|
124
213
|
export function agentBrief(raw = {}) {
|
|
125
214
|
const a = raw && typeof raw === 'object' ? raw : {};
|
|
215
|
+
const id = String(a.id || '');
|
|
126
216
|
for (const k of ['brief', 'instructions', 'customInstructions', 'systemPrompt']) {
|
|
127
217
|
const v = a[k];
|
|
128
|
-
if (typeof v === 'string' && v.trim()
|
|
218
|
+
if (typeof v === 'string' && v.trim() && !isStubBrief(v, id)) {
|
|
219
|
+
const name = displayName(a);
|
|
220
|
+
const text = v.trim();
|
|
221
|
+
if (id && name && !looksLikeAgentId(name, id) && text.startsWith(`You are ${id}`)) {
|
|
222
|
+
return (`You are ${name}` + text.slice(`You are ${id}`.length)).slice(0, 8000);
|
|
223
|
+
}
|
|
224
|
+
return text.slice(0, 8000);
|
|
225
|
+
}
|
|
129
226
|
}
|
|
130
227
|
const d = String(a.description || '').trim();
|
|
131
|
-
if (d) return d.slice(0, 8000);
|
|
132
|
-
return briefFromName(a
|
|
228
|
+
if (d && !isStubBrief(d, id)) return d.slice(0, 8000);
|
|
229
|
+
return briefFromName(displayName(a)).slice(0, 8000);
|
|
133
230
|
}
|
|
134
231
|
|
|
135
232
|
export function shapeAgent(raw = {}) {
|
|
@@ -139,14 +236,14 @@ export function shapeAgent(raw = {}) {
|
|
|
139
236
|
? a.memberIds.map((x) => String(x)).filter(Boolean)
|
|
140
237
|
: (Array.isArray(a.memberAgentIds) ? a.memberAgentIds.map((x) => String(x)).filter(Boolean) : []);
|
|
141
238
|
const isGroup = a.isGroup === true || memberIds.length > 0;
|
|
142
|
-
const name =
|
|
143
|
-
const brief = agentBrief(a);
|
|
239
|
+
const name = displayName({ ...a, isGroup, memberIds });
|
|
240
|
+
const brief = agentBrief({ ...a, name, title: a.title || name });
|
|
144
241
|
return {
|
|
145
242
|
id,
|
|
146
243
|
name,
|
|
147
244
|
brief,
|
|
148
|
-
description: String(a.description || brief || ''),
|
|
149
|
-
title: String(a.title || name),
|
|
245
|
+
description: String((a.description && !looksLikeAgentId(a.description, id) && a.description) || brief || ''),
|
|
246
|
+
title: String((!looksLikeAgentId(a.title, id) && a.title) || name),
|
|
150
247
|
origin: String(a.origin || 'user'),
|
|
151
248
|
path: String(a.path || (id ? `/local/${id}` : '/local')),
|
|
152
249
|
createdAt: Number(a.createdAt) || Date.now(),
|
package/lib/ozSpendChip.js
CHANGED
|
@@ -159,6 +159,7 @@ function ozEnsureSpendCss() {
|
|
|
159
159
|
}
|
|
160
160
|
s.textContent = [
|
|
161
161
|
'.oz-spend{margin:.55rem 0 0;font-size:12px;color:inherit;opacity:.82;max-width:36em}',
|
|
162
|
+
'#oz-spend-float>summary{cursor:grab}',
|
|
162
163
|
'.oz-spend>summary{cursor:help;list-style:none;display:inline-flex;align-items:center;gap:.35rem;',
|
|
163
164
|
'padding:3px 9px;border-radius:999px;border:1px solid rgba(255,255,255,.18);white-space:nowrap;',
|
|
164
165
|
'max-width:100%;overflow:hidden;text-overflow:ellipsis}',
|
|
@@ -339,27 +340,78 @@ function ozEnsureFloatSpend() {
|
|
|
339
340
|
document.body.appendChild(el);
|
|
340
341
|
}
|
|
341
342
|
ozPlaceFloat(el);
|
|
343
|
+
ozDragFloat(el);
|
|
342
344
|
const sum = el.querySelector('summary');
|
|
343
345
|
if (sum) sum.textContent = 'ⓘ ' + label;
|
|
344
346
|
}
|
|
345
347
|
|
|
348
|
+
function ozSavedFloatPos() {
|
|
349
|
+
try {
|
|
350
|
+
const p = JSON.parse(localStorage.getItem('oz-spend-float-pos') || 'null');
|
|
351
|
+
if (p && Number.isFinite(+p.left) && Number.isFinite(+p.top)) return { left: +p.left, top: +p.top };
|
|
352
|
+
} catch (e) {}
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
|
|
346
356
|
function ozPlaceFloat(el) {
|
|
347
357
|
if (!el) return;
|
|
348
|
-
const ta = document.querySelector('textarea, [contenteditable="true"]');
|
|
349
|
-
const box = ta && ta.getBoundingClientRect();
|
|
350
358
|
el.style.position = 'fixed';
|
|
351
|
-
el.style.right = 'auto';
|
|
352
359
|
el.style.zIndex = '2147483646';
|
|
353
360
|
el.style.opacity = '0.95';
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
el.style.
|
|
361
|
+
const saved = ozSavedFloatPos();
|
|
362
|
+
if (saved) {
|
|
363
|
+
el.style.left = Math.max(8, saved.left) + 'px';
|
|
364
|
+
el.style.top = Math.max(8, saved.top) + 'px';
|
|
365
|
+
el.style.right = 'auto';
|
|
357
366
|
el.style.bottom = 'auto';
|
|
358
|
-
|
|
359
|
-
el.style.left = '96px';
|
|
360
|
-
el.style.bottom = '72px';
|
|
361
|
-
el.style.top = 'auto';
|
|
367
|
+
return;
|
|
362
368
|
}
|
|
369
|
+
el.style.left = 'auto';
|
|
370
|
+
el.style.right = '16px';
|
|
371
|
+
el.style.top = '48px';
|
|
372
|
+
el.style.bottom = 'auto';
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function ozDragFloat(el) {
|
|
376
|
+
if (!el || el.dataset.ozDrag === '1') return;
|
|
377
|
+
el.dataset.ozDrag = '1';
|
|
378
|
+
const sum = el.querySelector('summary') || el;
|
|
379
|
+
let dragging = false;
|
|
380
|
+
let moved = false;
|
|
381
|
+
let dx = 0;
|
|
382
|
+
let dy = 0;
|
|
383
|
+
sum.addEventListener('pointerdown', (ev) => {
|
|
384
|
+
if (ev.button !== 0) return;
|
|
385
|
+
dragging = true;
|
|
386
|
+
moved = false;
|
|
387
|
+
const r = el.getBoundingClientRect();
|
|
388
|
+
dx = ev.clientX - r.left;
|
|
389
|
+
dy = ev.clientY - r.top;
|
|
390
|
+
el.style.cursor = 'grabbing';
|
|
391
|
+
try { sum.setPointerCapture(ev.pointerId); } catch (e) {}
|
|
392
|
+
ev.preventDefault();
|
|
393
|
+
ev.stopPropagation();
|
|
394
|
+
});
|
|
395
|
+
sum.addEventListener('pointermove', (ev) => {
|
|
396
|
+
if (!dragging) return;
|
|
397
|
+
moved = true;
|
|
398
|
+
el.style.left = Math.max(8, ev.clientX - dx) + 'px';
|
|
399
|
+
el.style.top = Math.max(8, ev.clientY - dy) + 'px';
|
|
400
|
+
el.style.right = 'auto';
|
|
401
|
+
el.style.bottom = 'auto';
|
|
402
|
+
});
|
|
403
|
+
const end = () => {
|
|
404
|
+
if (!dragging) return;
|
|
405
|
+
dragging = false;
|
|
406
|
+
el.style.cursor = 'grab';
|
|
407
|
+
const r = el.getBoundingClientRect();
|
|
408
|
+
try { localStorage.setItem('oz-spend-float-pos', JSON.stringify({ left: r.left, top: r.top })); } catch (e) {}
|
|
409
|
+
};
|
|
410
|
+
sum.addEventListener('pointerup', end);
|
|
411
|
+
sum.addEventListener('pointercancel', end);
|
|
412
|
+
sum.addEventListener('click', (ev) => {
|
|
413
|
+
if (moved) { ev.preventDefault(); ev.stopPropagation(); }
|
|
414
|
+
}, true);
|
|
363
415
|
}
|
|
364
416
|
|
|
365
417
|
function ozWatchSpend() {
|
|
@@ -385,20 +437,8 @@ export function spendChipSource() {
|
|
|
385
437
|
return [
|
|
386
438
|
'(function ozSpendChip(){',
|
|
387
439
|
"'use strict';",
|
|
388
|
-
'if (window.__OZ_SPEND_CHIP__ ===
|
|
389
|
-
'
|
|
390
|
-
' const ta = document.querySelector("textarea, [contenteditable=\\"true\\"]");',
|
|
391
|
-
' const box = ta && ta.getBoundingClientRect();',
|
|
392
|
-
' if (el && box && box.width > 80) {',
|
|
393
|
-
' el.style.position = "fixed";',
|
|
394
|
-
' el.style.left = Math.round(box.left) + "px";',
|
|
395
|
-
' el.style.top = Math.round(Math.max(8, box.top - 56)) + "px";',
|
|
396
|
-
' el.style.right = "auto";',
|
|
397
|
-
' el.style.bottom = "auto";',
|
|
398
|
-
' }',
|
|
399
|
-
' return;',
|
|
400
|
-
'}',
|
|
401
|
-
'window.__OZ_SPEND_CHIP__ = 11;',
|
|
440
|
+
'if (window.__OZ_SPEND_CHIP__ === 12) return;',
|
|
441
|
+
'window.__OZ_SPEND_CHIP__ = 12;',
|
|
402
442
|
chipUsd.toString(),
|
|
403
443
|
labelFromSpendBody.toString(),
|
|
404
444
|
spendLinesOnly.toString(),
|
|
@@ -414,7 +454,9 @@ export function spendChipSource() {
|
|
|
414
454
|
ozAttachSpendChip.toString(),
|
|
415
455
|
ozCollapseSpend.toString(),
|
|
416
456
|
ozEnsureFloatSpend.toString(),
|
|
457
|
+
ozSavedFloatPos.toString(),
|
|
417
458
|
ozPlaceFloat.toString(),
|
|
459
|
+
ozDragFloat.toString(),
|
|
418
460
|
ozWatchSpend.toString(),
|
|
419
461
|
'ozWatchSpend();',
|
|
420
462
|
'})();',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.54",
|
|
4
4
|
"description": "Local x402-paying proxy + MCP server for openzoo.fun — point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|