@vibevibes/sdk 0.1.0 → 0.3.0
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/LICENSE +21 -0
- package/README.md +200 -0
- package/dist/index.cjs +67 -688
- package/dist/index.d.cts +85 -501
- package/dist/index.d.ts +85 -501
- package/dist/index.js +64 -665
- package/package.json +69 -63
package/dist/index.js
CHANGED
|
@@ -12,709 +12,108 @@ function defineTool(config) {
|
|
|
12
12
|
function defineTest(config) {
|
|
13
13
|
return { name: config.name, run: config.run };
|
|
14
14
|
}
|
|
15
|
-
function
|
|
15
|
+
function defineStream(config) {
|
|
16
16
|
return {
|
|
17
17
|
name: config.name,
|
|
18
18
|
description: config.description,
|
|
19
|
-
input_schema: config.input_schema
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
function quickTool(name, description, input_schema, handler) {
|
|
23
|
-
return {
|
|
24
|
-
name,
|
|
25
|
-
description,
|
|
26
|
-
input_schema,
|
|
27
|
-
risk: "low",
|
|
28
|
-
capabilities_required: ["state.write"],
|
|
29
|
-
handler
|
|
19
|
+
input_schema: config.input_schema,
|
|
20
|
+
merge: config.merge,
|
|
21
|
+
rateLimit: config.rateLimit
|
|
30
22
|
};
|
|
31
23
|
}
|
|
32
24
|
function defineExperience(module) {
|
|
33
|
-
const m = module.manifest;
|
|
25
|
+
const m = module.manifest ?? {};
|
|
26
|
+
let initialState = module.initialState;
|
|
27
|
+
if (module.stateSchema && !initialState) {
|
|
28
|
+
try {
|
|
29
|
+
initialState = module.stateSchema.parse(void 0);
|
|
30
|
+
} catch {
|
|
31
|
+
try {
|
|
32
|
+
initialState = module.stateSchema.parse({});
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (module.stateSchema && initialState) {
|
|
38
|
+
try {
|
|
39
|
+
module.stateSchema.parse(initialState);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.warn(
|
|
42
|
+
`[vibevibes] initialState does not match stateSchema: ${err instanceof Error ? err.message : err}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const rawParticipantSlots = module.participants ?? m.participantSlots ?? (module.agents || m.agentSlots)?.map((a) => ({ ...a, type: "ai" }));
|
|
47
|
+
const hasOrchestrator = rawParticipantSlots?.some((s) => s.role === "orchestrator");
|
|
48
|
+
const participantSlots = rawParticipantSlots ? hasOrchestrator ? rawParticipantSlots : [...rawParticipantSlots, { role: "orchestrator", type: "ai", maxInstances: 1 }] : [{ role: "orchestrator", type: "ai", maxInstances: 1 }];
|
|
49
|
+
const agentSlots = participantSlots?.filter(
|
|
50
|
+
(s) => s.type === "ai" || s.type === void 0 || s.type === "any"
|
|
51
|
+
) ?? m.agentSlots ?? module.agents;
|
|
34
52
|
return {
|
|
35
53
|
...module,
|
|
54
|
+
initialState,
|
|
36
55
|
manifest: {
|
|
37
56
|
...m,
|
|
57
|
+
title: m.title || module.name || m.id,
|
|
38
58
|
version: m.version || "0.0.1",
|
|
39
|
-
requested_capabilities: m.requested_capabilities || ["state.write"]
|
|
59
|
+
requested_capabilities: m.requested_capabilities || ["state.write"],
|
|
60
|
+
participantSlots: participantSlots ?? m.participantSlots,
|
|
61
|
+
agentSlots: agentSlots ?? m.agentSlots
|
|
40
62
|
}
|
|
41
63
|
};
|
|
42
64
|
}
|
|
43
|
-
function validateExperience(module) {
|
|
44
|
-
const errors = [];
|
|
45
|
-
if (!module.manifest?.id) {
|
|
46
|
-
errors.push("manifest.id is required");
|
|
47
|
-
}
|
|
48
|
-
if (!module.manifest?.version) {
|
|
49
|
-
errors.push("manifest.version is required");
|
|
50
|
-
}
|
|
51
|
-
if (!module.manifest?.title) {
|
|
52
|
-
errors.push("manifest.title is required");
|
|
53
|
-
}
|
|
54
|
-
if (!module.Canvas) {
|
|
55
|
-
errors.push("Canvas component is required");
|
|
56
|
-
}
|
|
57
|
-
if (!Array.isArray(module.tools)) {
|
|
58
|
-
errors.push("tools must be an array");
|
|
59
|
-
} else {
|
|
60
|
-
module.tools.forEach((tool, idx) => {
|
|
61
|
-
if (!tool.name) {
|
|
62
|
-
errors.push(`tools[${idx}].name is required`);
|
|
63
|
-
}
|
|
64
|
-
if (!tool.input_schema) {
|
|
65
|
-
errors.push(`tools[${idx}].input_schema is required`);
|
|
66
|
-
}
|
|
67
|
-
if (typeof tool.handler !== "function") {
|
|
68
|
-
errors.push(`tools[${idx}].handler must be a function`);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
return {
|
|
73
|
-
valid: errors.length === 0,
|
|
74
|
-
errors
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
65
|
|
|
78
|
-
// src/
|
|
79
|
-
function getReact() {
|
|
80
|
-
const R = globalThis.React;
|
|
81
|
-
if (!R) throw new Error("React is not available. Hooks must be used inside a Canvas component.");
|
|
82
|
-
return R;
|
|
83
|
-
}
|
|
84
|
-
var React = new Proxy({}, {
|
|
85
|
-
get(_target, prop) {
|
|
86
|
-
return getReact()[prop];
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
function useToolCall(callTool) {
|
|
90
|
-
const [loading, setLoading] = React.useState(false);
|
|
91
|
-
const [error, setError] = React.useState(null);
|
|
92
|
-
const call = React.useCallback(
|
|
93
|
-
async (name, input) => {
|
|
94
|
-
setLoading(true);
|
|
95
|
-
setError(null);
|
|
96
|
-
try {
|
|
97
|
-
const result = await callTool(name, input);
|
|
98
|
-
return result;
|
|
99
|
-
} catch (err) {
|
|
100
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
101
|
-
setError(msg);
|
|
102
|
-
throw err;
|
|
103
|
-
} finally {
|
|
104
|
-
setLoading(false);
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
[callTool]
|
|
108
|
-
);
|
|
109
|
-
return { call, loading, error };
|
|
110
|
-
}
|
|
111
|
-
function useSharedState(sharedState, key, defaultValue) {
|
|
112
|
-
const value = sharedState[key];
|
|
113
|
-
if (value === void 0) return defaultValue;
|
|
114
|
-
return value;
|
|
115
|
-
}
|
|
116
|
-
function useOptimisticTool(callTool, sharedState) {
|
|
117
|
-
const [optimistic, setOptimistic] = React.useState(null);
|
|
118
|
-
const [pending, setPending] = React.useState(false);
|
|
119
|
-
const call = React.useCallback(
|
|
120
|
-
async (name, input, optimisticState) => {
|
|
121
|
-
setOptimistic(optimisticState);
|
|
122
|
-
setPending(true);
|
|
123
|
-
try {
|
|
124
|
-
const result = await callTool(name, input);
|
|
125
|
-
setOptimistic(null);
|
|
126
|
-
return result;
|
|
127
|
-
} catch (err) {
|
|
128
|
-
setOptimistic(null);
|
|
129
|
-
throw err;
|
|
130
|
-
} finally {
|
|
131
|
-
setPending(false);
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
[callTool]
|
|
135
|
-
);
|
|
136
|
-
const state = optimistic ? { ...sharedState, ...optimistic } : sharedState;
|
|
137
|
-
return { call, state, pending };
|
|
138
|
-
}
|
|
139
|
-
function useAnimationFrame(sharedState, interpolate) {
|
|
140
|
-
const prevStateRef = React.useRef(sharedState);
|
|
141
|
-
const targetStateRef = React.useRef(sharedState);
|
|
142
|
-
const displayStateRef = React.useRef(sharedState);
|
|
143
|
-
const [displayState, setDisplayState] = React.useState(sharedState);
|
|
144
|
-
const rafRef = React.useRef(null);
|
|
145
|
-
const transitionStartRef = React.useRef(0);
|
|
146
|
-
const TRANSITION_MS = 50;
|
|
147
|
-
React.useEffect(() => {
|
|
148
|
-
prevStateRef.current = displayStateRef.current;
|
|
149
|
-
targetStateRef.current = sharedState;
|
|
150
|
-
transitionStartRef.current = performance.now();
|
|
151
|
-
if (!rafRef.current) {
|
|
152
|
-
const tick = () => {
|
|
153
|
-
const now = performance.now();
|
|
154
|
-
const elapsed = now - transitionStartRef.current;
|
|
155
|
-
const t = Math.min(elapsed / TRANSITION_MS, 1);
|
|
156
|
-
let next;
|
|
157
|
-
if (interpolate && t < 1) {
|
|
158
|
-
next = interpolate(prevStateRef.current, targetStateRef.current, t);
|
|
159
|
-
} else {
|
|
160
|
-
next = targetStateRef.current;
|
|
161
|
-
}
|
|
162
|
-
displayStateRef.current = next;
|
|
163
|
-
setDisplayState(next);
|
|
164
|
-
if (t < 1 && interpolate) {
|
|
165
|
-
rafRef.current = requestAnimationFrame(tick);
|
|
166
|
-
} else {
|
|
167
|
-
rafRef.current = null;
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
rafRef.current = requestAnimationFrame(tick);
|
|
171
|
-
}
|
|
172
|
-
}, [sharedState, interpolate]);
|
|
173
|
-
React.useEffect(() => {
|
|
174
|
-
return () => {
|
|
175
|
-
if (rafRef.current) {
|
|
176
|
-
cancelAnimationFrame(rafRef.current);
|
|
177
|
-
rafRef.current = null;
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
}, []);
|
|
181
|
-
return displayState;
|
|
182
|
-
}
|
|
183
|
-
function useParticipants(participants) {
|
|
184
|
-
return React.useMemo(() => {
|
|
185
|
-
return participants.map((id) => {
|
|
186
|
-
const match = id.match(/^(.+)-(human|ai)-(\d+)$/);
|
|
187
|
-
if (match) {
|
|
188
|
-
return {
|
|
189
|
-
id,
|
|
190
|
-
username: match[1],
|
|
191
|
-
type: match[2],
|
|
192
|
-
index: parseInt(match[3], 10)
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
return { id, username: id, type: "unknown", index: 0 };
|
|
196
|
-
});
|
|
197
|
-
}, [participants]);
|
|
198
|
-
}
|
|
199
|
-
function useFollow(actorId, participants, ephemeralState, setEphemeral, _onEphemeralAction, dispatchEphemeralAction) {
|
|
200
|
-
const myEphemeral = ephemeralState[actorId];
|
|
201
|
-
const following = myEphemeral?._follow ?? null;
|
|
202
|
-
const followers = React.useMemo(() => {
|
|
203
|
-
const result = [];
|
|
204
|
-
for (const pid of participants) {
|
|
205
|
-
if (pid === actorId) continue;
|
|
206
|
-
const peerFollow = ephemeralState[pid]?._follow;
|
|
207
|
-
if (peerFollow && peerFollow.targetActorId === actorId) {
|
|
208
|
-
result.push({
|
|
209
|
-
actorId: pid,
|
|
210
|
-
mode: peerFollow.mode,
|
|
211
|
-
since: peerFollow.since
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return result;
|
|
216
|
-
}, [participants, ephemeralState, actorId]);
|
|
217
|
-
const follow = React.useCallback(
|
|
218
|
-
(targetActorId, mode) => {
|
|
219
|
-
if (targetActorId === actorId) return;
|
|
220
|
-
const followData = {
|
|
221
|
-
targetActorId,
|
|
222
|
-
mode,
|
|
223
|
-
since: Date.now()
|
|
224
|
-
};
|
|
225
|
-
setEphemeral({ _follow: followData });
|
|
226
|
-
if (dispatchEphemeralAction) {
|
|
227
|
-
dispatchEphemeralAction("follow.started", {
|
|
228
|
-
follower: actorId,
|
|
229
|
-
target: targetActorId,
|
|
230
|
-
mode
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
},
|
|
234
|
-
[actorId, setEphemeral, dispatchEphemeralAction]
|
|
235
|
-
);
|
|
236
|
-
const unfollow = React.useCallback(() => {
|
|
237
|
-
const currentTarget = following?.targetActorId;
|
|
238
|
-
setEphemeral({ _follow: null });
|
|
239
|
-
if (dispatchEphemeralAction && currentTarget) {
|
|
240
|
-
dispatchEphemeralAction("follow.stopped", {
|
|
241
|
-
follower: actorId,
|
|
242
|
-
target: currentTarget
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
}, [actorId, following, setEphemeral, dispatchEphemeralAction]);
|
|
246
|
-
React.useEffect(() => {
|
|
247
|
-
if (!following) return;
|
|
248
|
-
if (!participants.includes(following.targetActorId)) {
|
|
249
|
-
setEphemeral({ _follow: null });
|
|
250
|
-
}
|
|
251
|
-
}, [following, participants, setEphemeral]);
|
|
252
|
-
return { follow, unfollow, following, followers };
|
|
253
|
-
}
|
|
254
|
-
function useTypingIndicator(actorId, ephemeralState, setEphemeral, timeoutMs = 3e3) {
|
|
255
|
-
const timerRef = React.useRef(null);
|
|
256
|
-
const setTyping = React.useCallback((isTyping) => {
|
|
257
|
-
setEphemeral({ _typing: isTyping ? Date.now() : null });
|
|
258
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
259
|
-
if (isTyping) {
|
|
260
|
-
timerRef.current = setTimeout(() => {
|
|
261
|
-
setEphemeral({ _typing: null });
|
|
262
|
-
}, timeoutMs);
|
|
263
|
-
}
|
|
264
|
-
}, [setEphemeral, timeoutMs]);
|
|
265
|
-
React.useEffect(() => {
|
|
266
|
-
return () => {
|
|
267
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
268
|
-
};
|
|
269
|
-
}, []);
|
|
270
|
-
const now = Date.now();
|
|
271
|
-
const typingUsers = Object.entries(ephemeralState).filter(([id, data]) => id !== actorId && data._typing && now - data._typing < timeoutMs + 2e3).map(([id]) => id);
|
|
272
|
-
return { setTyping, typingUsers };
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// src/components.ts
|
|
276
|
-
function getReact2() {
|
|
277
|
-
const R = globalThis.React;
|
|
278
|
-
if (!R) throw new Error("React is not available.");
|
|
279
|
-
return R;
|
|
280
|
-
}
|
|
281
|
-
function h(type, props, ...children) {
|
|
282
|
-
return getReact2().createElement(type, props, ...children);
|
|
283
|
-
}
|
|
284
|
-
var buttonColors = {
|
|
285
|
-
primary: { bg: "#6366f1", text: "#fff", hover: "#4f46e5" },
|
|
286
|
-
secondary: { bg: "#f3f4f6", text: "#374151", hover: "#e5e7eb" },
|
|
287
|
-
danger: { bg: "#ef4444", text: "#fff", hover: "#dc2626" },
|
|
288
|
-
ghost: { bg: "transparent", text: "#6b7280", hover: "#f3f4f6" }
|
|
289
|
-
};
|
|
290
|
-
var buttonSizes = {
|
|
291
|
-
sm: { padding: "0.375rem 0.75rem", fontSize: "0.8125rem" },
|
|
292
|
-
md: { padding: "0.5rem 1rem", fontSize: "0.875rem" },
|
|
293
|
-
lg: { padding: "0.625rem 1.25rem", fontSize: "1rem" }
|
|
294
|
-
};
|
|
295
|
-
function Button({ children, onClick, disabled, variant = "primary", size = "md", style }) {
|
|
296
|
-
const colors = buttonColors[variant];
|
|
297
|
-
const sizeStyles = buttonSizes[size];
|
|
298
|
-
return h("button", {
|
|
299
|
-
onClick,
|
|
300
|
-
disabled,
|
|
301
|
-
style: {
|
|
302
|
-
...sizeStyles,
|
|
303
|
-
backgroundColor: colors.bg,
|
|
304
|
-
color: colors.text,
|
|
305
|
-
border: "none",
|
|
306
|
-
borderRadius: "0.5rem",
|
|
307
|
-
fontWeight: 500,
|
|
308
|
-
cursor: disabled ? "not-allowed" : "pointer",
|
|
309
|
-
opacity: disabled ? 0.5 : 1,
|
|
310
|
-
transition: "background-color 0.15s, opacity 0.15s",
|
|
311
|
-
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
312
|
-
lineHeight: 1.5,
|
|
313
|
-
...style
|
|
314
|
-
}
|
|
315
|
-
}, children);
|
|
316
|
-
}
|
|
317
|
-
function Card({ children, title, style }) {
|
|
318
|
-
return h(
|
|
319
|
-
"div",
|
|
320
|
-
{
|
|
321
|
-
style: {
|
|
322
|
-
backgroundColor: "#fff",
|
|
323
|
-
border: "1px solid #e5e7eb",
|
|
324
|
-
borderRadius: "0.75rem",
|
|
325
|
-
padding: "1.25rem",
|
|
326
|
-
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
327
|
-
...style
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
title ? h("h3", {
|
|
331
|
-
style: { margin: "0 0 0.75rem 0", fontSize: "1rem", fontWeight: 600, color: "#111827" }
|
|
332
|
-
}, title) : null,
|
|
333
|
-
children
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
function Input({ value, onChange, placeholder, type = "text", disabled, style }) {
|
|
337
|
-
return h("input", {
|
|
338
|
-
type,
|
|
339
|
-
value,
|
|
340
|
-
placeholder,
|
|
341
|
-
disabled,
|
|
342
|
-
onChange: onChange ? (e) => onChange(e.target.value) : void 0,
|
|
343
|
-
style: {
|
|
344
|
-
width: "100%",
|
|
345
|
-
padding: "0.5rem 0.75rem",
|
|
346
|
-
fontSize: "0.875rem",
|
|
347
|
-
border: "1px solid #d1d5db",
|
|
348
|
-
borderRadius: "0.5rem",
|
|
349
|
-
outline: "none",
|
|
350
|
-
backgroundColor: disabled ? "#f9fafb" : "#fff",
|
|
351
|
-
color: "#111827",
|
|
352
|
-
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
353
|
-
lineHeight: 1.5,
|
|
354
|
-
boxSizing: "border-box",
|
|
355
|
-
...style
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
var badgeColors = {
|
|
360
|
-
gray: { bg: "#f3f4f6", text: "#374151" },
|
|
361
|
-
blue: { bg: "#dbeafe", text: "#1d4ed8" },
|
|
362
|
-
green: { bg: "#dcfce7", text: "#15803d" },
|
|
363
|
-
red: { bg: "#fee2e2", text: "#b91c1c" },
|
|
364
|
-
yellow: { bg: "#fef9c3", text: "#a16207" },
|
|
365
|
-
purple: { bg: "#f3e8ff", text: "#7e22ce" }
|
|
366
|
-
};
|
|
367
|
-
function Badge({ children, color = "gray", style }) {
|
|
368
|
-
const colors = badgeColors[color];
|
|
369
|
-
return h("span", {
|
|
370
|
-
style: {
|
|
371
|
-
display: "inline-flex",
|
|
372
|
-
alignItems: "center",
|
|
373
|
-
padding: "0.125rem 0.625rem",
|
|
374
|
-
fontSize: "0.75rem",
|
|
375
|
-
fontWeight: 500,
|
|
376
|
-
borderRadius: "9999px",
|
|
377
|
-
backgroundColor: colors.bg,
|
|
378
|
-
color: colors.text,
|
|
379
|
-
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
380
|
-
lineHeight: 1.5,
|
|
381
|
-
...style
|
|
382
|
-
}
|
|
383
|
-
}, children);
|
|
384
|
-
}
|
|
385
|
-
function Stack({ children, direction = "column", gap = "0.5rem", align, justify, style }) {
|
|
386
|
-
return h("div", {
|
|
387
|
-
style: {
|
|
388
|
-
display: "flex",
|
|
389
|
-
flexDirection: direction,
|
|
390
|
-
gap,
|
|
391
|
-
alignItems: align,
|
|
392
|
-
justifyContent: justify,
|
|
393
|
-
...style
|
|
394
|
-
}
|
|
395
|
-
}, children);
|
|
396
|
-
}
|
|
397
|
-
function Grid({ children, columns = 2, gap = "0.75rem", style }) {
|
|
398
|
-
return h("div", {
|
|
399
|
-
style: {
|
|
400
|
-
display: "grid",
|
|
401
|
-
gridTemplateColumns: typeof columns === "number" ? `repeat(${columns}, 1fr)` : columns,
|
|
402
|
-
gap,
|
|
403
|
-
...style
|
|
404
|
-
}
|
|
405
|
-
}, children);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// src/agent-protocol.ts
|
|
66
|
+
// src/chat.ts
|
|
409
67
|
function uid() {
|
|
410
68
|
return Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
|
|
411
69
|
}
|
|
412
|
-
function capMessages(msgs, max =
|
|
70
|
+
function capMessages(msgs, max = 200) {
|
|
413
71
|
return msgs.length > max ? msgs.slice(-max) : msgs;
|
|
414
72
|
}
|
|
415
|
-
function
|
|
73
|
+
function createChatTools(z) {
|
|
416
74
|
return [
|
|
417
75
|
{
|
|
418
|
-
name:
|
|
419
|
-
description: "
|
|
76
|
+
name: "_chat.send",
|
|
77
|
+
description: "Send a chat message",
|
|
420
78
|
input_schema: z.object({
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
requiresVotes: z.number().optional()
|
|
79
|
+
message: z.string().min(1).max(2e3),
|
|
80
|
+
replyTo: z.string().optional()
|
|
424
81
|
}),
|
|
425
82
|
risk: "low",
|
|
426
83
|
capabilities_required: ["state.write"],
|
|
427
84
|
handler: async (ctx, input) => {
|
|
428
|
-
const
|
|
429
|
-
const proposals = { ...ctx.state._agentProposals || {} };
|
|
430
|
-
proposals[id] = {
|
|
431
|
-
id,
|
|
432
|
-
from: ctx.actorId,
|
|
433
|
-
proposal: input.proposal,
|
|
434
|
-
data: input.data,
|
|
435
|
-
requiresVotes: input.requiresVotes || 1,
|
|
436
|
-
votes: [],
|
|
437
|
-
status: "pending",
|
|
438
|
-
ts: ctx.timestamp
|
|
439
|
-
};
|
|
85
|
+
const cleanMessage = input.message.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
440
86
|
const messages = capMessages([
|
|
441
|
-
...ctx.state.
|
|
442
|
-
{
|
|
87
|
+
...ctx.state._chat || [],
|
|
88
|
+
{
|
|
89
|
+
id: uid(),
|
|
90
|
+
actorId: ctx.actorId,
|
|
91
|
+
message: cleanMessage,
|
|
92
|
+
replyTo: input.replyTo,
|
|
93
|
+
ts: ctx.timestamp
|
|
94
|
+
}
|
|
443
95
|
]);
|
|
444
|
-
ctx.setState({ ...ctx.state,
|
|
445
|
-
return {
|
|
96
|
+
ctx.setState({ ...ctx.state, _chat: messages });
|
|
97
|
+
return { sent: true, messageCount: messages.length };
|
|
446
98
|
}
|
|
447
99
|
},
|
|
448
100
|
{
|
|
449
|
-
name:
|
|
450
|
-
description: "
|
|
451
|
-
input_schema: z.object({
|
|
452
|
-
|
|
453
|
-
vote: z.enum(["approve", "reject"]),
|
|
454
|
-
reason: z.string().optional()
|
|
455
|
-
}),
|
|
456
|
-
risk: "low",
|
|
101
|
+
name: "_chat.clear",
|
|
102
|
+
description: "Clear all chat messages",
|
|
103
|
+
input_schema: z.object({}),
|
|
104
|
+
risk: "medium",
|
|
457
105
|
capabilities_required: ["state.write"],
|
|
458
|
-
handler: async (ctx
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
if (!proposal) throw new Error(`Proposal ${input.proposalId} not found`);
|
|
462
|
-
if (proposal.status !== "pending") throw new Error(`Proposal already ${proposal.status}`);
|
|
463
|
-
if (proposal.votes.some((v) => v.actorId === ctx.actorId)) throw new Error("Already voted");
|
|
464
|
-
const votes = [...proposal.votes, { actorId: ctx.actorId, vote: input.vote, reason: input.reason }];
|
|
465
|
-
const approves = votes.filter((v) => v.vote === "approve").length;
|
|
466
|
-
const rejects = votes.filter((v) => v.vote === "reject").length;
|
|
467
|
-
let status = "pending";
|
|
468
|
-
if (approves >= proposal.requiresVotes) status = "approved";
|
|
469
|
-
else if (rejects >= proposal.requiresVotes) status = "rejected";
|
|
470
|
-
proposals[input.proposalId] = { ...proposal, votes, status };
|
|
471
|
-
const messages = capMessages([
|
|
472
|
-
...ctx.state._agentMessages || [],
|
|
473
|
-
{ id: uid(), from: ctx.actorId, to: proposal.from, type: "vote", content: `${input.vote}: ${input.reason || ""}`, data: { proposalId: input.proposalId, vote: input.vote }, ts: ctx.timestamp }
|
|
474
|
-
]);
|
|
475
|
-
ctx.setState({ ...ctx.state, _agentProposals: proposals, _agentMessages: messages });
|
|
476
|
-
return { status, voteCount: votes.length };
|
|
477
|
-
}
|
|
478
|
-
},
|
|
479
|
-
{
|
|
480
|
-
name: `${namespace}.agent.delegate`,
|
|
481
|
-
description: "Delegate a task to another agent",
|
|
482
|
-
input_schema: z.object({
|
|
483
|
-
targetActorId: z.string(),
|
|
484
|
-
task: z.string(),
|
|
485
|
-
data: z.any().optional()
|
|
486
|
-
}),
|
|
487
|
-
risk: "low",
|
|
488
|
-
capabilities_required: ["state.write"],
|
|
489
|
-
handler: async (ctx, input) => {
|
|
490
|
-
const id = uid();
|
|
491
|
-
const messages = capMessages([
|
|
492
|
-
...ctx.state._agentMessages || [],
|
|
493
|
-
{ id, from: ctx.actorId, to: input.targetActorId, type: "delegate", content: input.task, data: input.data, ts: ctx.timestamp }
|
|
494
|
-
]);
|
|
495
|
-
ctx.setState({ ...ctx.state, _agentMessages: messages });
|
|
496
|
-
return { messageId: id };
|
|
497
|
-
}
|
|
498
|
-
},
|
|
499
|
-
{
|
|
500
|
-
name: `${namespace}.agent.inform`,
|
|
501
|
-
description: "Share information with other agents",
|
|
502
|
-
input_schema: z.object({
|
|
503
|
-
to: z.string(),
|
|
504
|
-
message: z.string(),
|
|
505
|
-
data: z.any().optional()
|
|
506
|
-
}),
|
|
507
|
-
risk: "low",
|
|
508
|
-
capabilities_required: ["state.write"],
|
|
509
|
-
handler: async (ctx, input) => {
|
|
510
|
-
const id = uid();
|
|
511
|
-
const messages = capMessages([
|
|
512
|
-
...ctx.state._agentMessages || [],
|
|
513
|
-
{ id, from: ctx.actorId, to: input.to, type: "inform", content: input.message, data: input.data, ts: ctx.timestamp }
|
|
514
|
-
]);
|
|
515
|
-
ctx.setState({ ...ctx.state, _agentMessages: messages });
|
|
516
|
-
return { messageId: id };
|
|
517
|
-
}
|
|
518
|
-
},
|
|
519
|
-
{
|
|
520
|
-
name: `${namespace}.agent.request`,
|
|
521
|
-
description: "Request an action from another agent",
|
|
522
|
-
input_schema: z.object({
|
|
523
|
-
targetActorId: z.string(),
|
|
524
|
-
request: z.string(),
|
|
525
|
-
data: z.any().optional()
|
|
526
|
-
}),
|
|
527
|
-
risk: "low",
|
|
528
|
-
capabilities_required: ["state.write"],
|
|
529
|
-
handler: async (ctx, input) => {
|
|
530
|
-
const id = uid();
|
|
531
|
-
const messages = capMessages([
|
|
532
|
-
...ctx.state._agentMessages || [],
|
|
533
|
-
{ id, from: ctx.actorId, to: input.targetActorId, type: "request", content: input.request, data: input.data, ts: ctx.timestamp }
|
|
534
|
-
]);
|
|
535
|
-
ctx.setState({ ...ctx.state, _agentMessages: messages });
|
|
536
|
-
return { messageId: id };
|
|
537
|
-
}
|
|
538
|
-
},
|
|
539
|
-
{
|
|
540
|
-
name: `${namespace}.agent.respond`,
|
|
541
|
-
description: "Respond to a message, request, or delegation from another agent",
|
|
542
|
-
input_schema: z.object({
|
|
543
|
-
messageId: z.string(),
|
|
544
|
-
response: z.string(),
|
|
545
|
-
data: z.any().optional()
|
|
546
|
-
}),
|
|
547
|
-
risk: "low",
|
|
548
|
-
capabilities_required: ["state.write"],
|
|
549
|
-
handler: async (ctx, input) => {
|
|
550
|
-
const id = uid();
|
|
551
|
-
const messages = capMessages([
|
|
552
|
-
...ctx.state._agentMessages || [],
|
|
553
|
-
{ id, from: ctx.actorId, to: "*", type: "inform", content: input.response, data: { ...input.data, inReplyTo: input.messageId }, ts: ctx.timestamp }
|
|
554
|
-
]);
|
|
555
|
-
ctx.setState({ ...ctx.state, _agentMessages: messages });
|
|
556
|
-
return { messageId: id };
|
|
106
|
+
handler: async (ctx) => {
|
|
107
|
+
ctx.setState({ ...ctx.state, _chat: [] });
|
|
108
|
+
return { cleared: true };
|
|
557
109
|
}
|
|
558
110
|
}
|
|
559
111
|
];
|
|
560
112
|
}
|
|
561
|
-
|
|
562
|
-
// src/agent-hints.ts
|
|
563
|
-
function createAgentProtocolHints(namespace) {
|
|
564
|
-
return [
|
|
565
|
-
{
|
|
566
|
-
trigger: "A proposal is pending and you have not voted on it yet",
|
|
567
|
-
condition: `Object.values(state._agentProposals || {}).some(p => p.status === 'pending' && !p.votes.some(v => v.actorId === actorId))`,
|
|
568
|
-
suggestedTools: [`${namespace}.agent.vote`],
|
|
569
|
-
priority: "high",
|
|
570
|
-
cooldownMs: 2e3
|
|
571
|
-
},
|
|
572
|
-
{
|
|
573
|
-
trigger: "You received a delegation addressed to you",
|
|
574
|
-
condition: `(state._agentMessages || []).some(m => m.type === 'delegate' && m.to === actorId)`,
|
|
575
|
-
suggestedTools: [`${namespace}.agent.respond`],
|
|
576
|
-
priority: "high",
|
|
577
|
-
cooldownMs: 3e3
|
|
578
|
-
},
|
|
579
|
-
{
|
|
580
|
-
trigger: "You received a request addressed to you",
|
|
581
|
-
condition: `(state._agentMessages || []).some(m => m.type === 'request' && m.to === actorId)`,
|
|
582
|
-
suggestedTools: [`${namespace}.agent.respond`],
|
|
583
|
-
priority: "medium",
|
|
584
|
-
cooldownMs: 2e3
|
|
585
|
-
},
|
|
586
|
-
{
|
|
587
|
-
trigger: "You have useful information to share with other agents",
|
|
588
|
-
suggestedTools: [`${namespace}.agent.inform`],
|
|
589
|
-
priority: "low",
|
|
590
|
-
cooldownMs: 5e3
|
|
591
|
-
},
|
|
592
|
-
{
|
|
593
|
-
trigger: "You want to propose a collaborative action for the group",
|
|
594
|
-
suggestedTools: [`${namespace}.agent.propose`],
|
|
595
|
-
priority: "low",
|
|
596
|
-
cooldownMs: 1e4
|
|
597
|
-
}
|
|
598
|
-
];
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// src/migrations.ts
|
|
602
|
-
function getStateVersion(state) {
|
|
603
|
-
return typeof state._version === "string" ? state._version : "0.0.0";
|
|
604
|
-
}
|
|
605
|
-
function compareSemver(a, b) {
|
|
606
|
-
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
607
|
-
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
608
|
-
const len = Math.max(pa.length, pb.length);
|
|
609
|
-
for (let i = 0; i < len; i++) {
|
|
610
|
-
const va = pa[i] || 0;
|
|
611
|
-
const vb = pb[i] || 0;
|
|
612
|
-
if (va < vb) return -1;
|
|
613
|
-
if (va > vb) return 1;
|
|
614
|
-
}
|
|
615
|
-
return 0;
|
|
616
|
-
}
|
|
617
|
-
function migrateState(state, migrations, currentVersion) {
|
|
618
|
-
const fromVersion = getStateVersion(state);
|
|
619
|
-
if (compareSemver(fromVersion, currentVersion) >= 0) {
|
|
620
|
-
return {
|
|
621
|
-
migrated: false,
|
|
622
|
-
fromVersion,
|
|
623
|
-
toVersion: fromVersion,
|
|
624
|
-
state
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
if (!migrations || migrations.length === 0) {
|
|
628
|
-
return {
|
|
629
|
-
migrated: false,
|
|
630
|
-
fromVersion,
|
|
631
|
-
toVersion: currentVersion,
|
|
632
|
-
state: { ...state, _version: currentVersion }
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
const sorted = [...migrations].sort((a, b) => compareSemver(a.to, b.to));
|
|
636
|
-
const applicable = sorted.filter(
|
|
637
|
-
(m) => compareSemver(m.to, fromVersion) > 0 && compareSemver(m.to, currentVersion) <= 0
|
|
638
|
-
);
|
|
639
|
-
if (applicable.length === 0) {
|
|
640
|
-
return {
|
|
641
|
-
migrated: false,
|
|
642
|
-
fromVersion,
|
|
643
|
-
toVersion: currentVersion,
|
|
644
|
-
state: { ...state, _version: currentVersion }
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
let migratedState = { ...state };
|
|
648
|
-
for (const migration of applicable) {
|
|
649
|
-
migratedState = migration.migrate(migratedState);
|
|
650
|
-
}
|
|
651
|
-
migratedState._version = currentVersion;
|
|
652
|
-
return {
|
|
653
|
-
migrated: true,
|
|
654
|
-
fromVersion,
|
|
655
|
-
toVersion: currentVersion,
|
|
656
|
-
state: migratedState
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
// src/storage.ts
|
|
661
|
-
var InMemoryAdapter = class {
|
|
662
|
-
roomStates = /* @__PURE__ */ new Map();
|
|
663
|
-
events = /* @__PURE__ */ new Map();
|
|
664
|
-
profiles = /* @__PURE__ */ new Map();
|
|
665
|
-
async saveRoomState(roomId, state) {
|
|
666
|
-
this.roomStates.set(roomId, state);
|
|
667
|
-
}
|
|
668
|
-
async loadRoomState(roomId) {
|
|
669
|
-
return this.roomStates.get(roomId) || null;
|
|
670
|
-
}
|
|
671
|
-
async appendEvent(roomId, event) {
|
|
672
|
-
if (!this.events.has(roomId)) {
|
|
673
|
-
this.events.set(roomId, []);
|
|
674
|
-
}
|
|
675
|
-
this.events.get(roomId).push(event);
|
|
676
|
-
}
|
|
677
|
-
async loadEvents(roomId, limit) {
|
|
678
|
-
const events = this.events.get(roomId) || [];
|
|
679
|
-
if (limit) {
|
|
680
|
-
return events.slice(-limit);
|
|
681
|
-
}
|
|
682
|
-
return events;
|
|
683
|
-
}
|
|
684
|
-
async listExperiences(_userId) {
|
|
685
|
-
return [];
|
|
686
|
-
}
|
|
687
|
-
async saveUserProfile(userId, profile) {
|
|
688
|
-
this.profiles.set(userId, profile);
|
|
689
|
-
}
|
|
690
|
-
async loadUserProfile(userId) {
|
|
691
|
-
return this.profiles.get(userId) || null;
|
|
692
|
-
}
|
|
693
|
-
};
|
|
694
113
|
export {
|
|
695
|
-
|
|
696
|
-
Button,
|
|
697
|
-
Card,
|
|
698
|
-
Grid,
|
|
699
|
-
InMemoryAdapter,
|
|
700
|
-
Input,
|
|
701
|
-
Stack,
|
|
702
|
-
compareSemver,
|
|
703
|
-
createAgentProtocolHints,
|
|
704
|
-
createAgentProtocolTools,
|
|
705
|
-
defineEphemeralAction,
|
|
114
|
+
createChatTools,
|
|
706
115
|
defineExperience,
|
|
116
|
+
defineStream,
|
|
707
117
|
defineTest,
|
|
708
|
-
defineTool
|
|
709
|
-
getStateVersion,
|
|
710
|
-
migrateState,
|
|
711
|
-
quickTool,
|
|
712
|
-
useAnimationFrame,
|
|
713
|
-
useFollow,
|
|
714
|
-
useOptimisticTool,
|
|
715
|
-
useParticipants,
|
|
716
|
-
useSharedState,
|
|
717
|
-
useToolCall,
|
|
718
|
-
useTypingIndicator,
|
|
719
|
-
validateExperience
|
|
118
|
+
defineTool
|
|
720
119
|
};
|