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