life
Version:
Life.js is the first fullstack framework to build agentic web applications. It is minimal, extensible, and typesafe. Well, everything you love.
351 lines (345 loc) • 10.5 kB
JavaScript
import {
createLifeClient,
parseAgentClientParam
} from "../chunk-T6NMMENO.mjs";
import "../chunk-5BLN2MK4.mjs";
import "../chunk-VF3CTIUM.mjs";
import "../chunk-HZQWVS5S.mjs";
import "../chunk-6CBODJTF.mjs";
import {
toMethodName
} from "../chunk-X455LF5V.mjs";
import {
zodObjectWithTelemetry
} from "../chunk-D2T23PCX.mjs";
import "../chunk-ZHBK6UTM.mjs";
import {
__name
} from "../chunk-2D3UJWOA.mjs";
// agent/client/builder.ts
var AgentClientBuilder = class _AgentClientBuilder {
static {
__name(this, "AgentClientBuilder");
}
def;
constructor(definition) {
this.def = definition;
}
/**
* Register plugins to extend the agent client features.
* Defaults to `[generation, memories, stores, actions, percepts]` plugins if not specified.
*
* In case you want to register custom plugins and still keep the defaults you can do:
* ```ts
* import { defaults } from "life/server";
*
* defineAgent("my-agent").plugins([...defaults.plugins, myCustomPlugin]);
* ```
*
* Or if you want only some of the defaults, you can do:
* ```ts
* import { defaults } from "life/server";
*
* defineAgent("my-agent").plugins([defaults.plugins.generation, defaults.plugins.memories]);
* ```
*/
plugins(plugins) {
const builder = new _AgentClientBuilder({ ...this.def, plugins: plugins.map((p) => p.def) });
const builderWithPlugins = _AgentClientBuilder.withPluginsMethods(builder);
return builderWithPlugins;
}
// biome-ignore lint/suspicious/noExplicitAny: reason
static withPluginsMethods(builder) {
for (const plugin of builder.def.plugins) {
Object.assign(builder, {
[toMethodName(plugin.name)]: (config) => {
const newBuilder = new _AgentClientBuilder({
...builder.def,
pluginConfigs: {
...builder.def.pluginConfigs ?? {},
[plugin.name]: config
}
});
return _AgentClientBuilder.withPluginsMethods(newBuilder);
}
});
}
return builder;
}
};
function defineAgentClient(name) {
const defaultDefinition = {
name,
plugins: [...defaults.plugins].map((p) => p.def),
pluginConfigs: {},
$serverDef: {}
};
const builder = new AgentClientBuilder(defaultDefinition);
const builderWithPlugins = AgentClientBuilder.withPluginsMethods(builder);
return builderWithPlugins;
}
__name(defineAgentClient, "defineAgentClient");
// plugins/client/builder.ts
import { z } from "zod";
var PluginClientBuilder = class _PluginClientBuilder {
static {
__name(this, "PluginClientBuilder");
}
def;
constructor(definition) {
this.def = definition;
}
/**
* ### `.dependencies()`
*
* Specify other plugins clients as required by this plugin client.
*
* Their config, atoms, and class can then be accessed from `dependencies.*` inside the plugin client's class and atoms.
*
* @see TODO: Add docs link
*
* @example
* ```ts
* const pluginClient = definePluginClient("my-plugin")
* .dependencies([anotherPluginClient])
* .atoms(({ dependencies }) => {
* // Obtain the atoms of the dependency plugin client
* const anotherPluginAtoms = dependencies.anotherPlugin.atoms();
* // ...
* });
* ```
*
*
* ---
* @param plugins - The dependencies definitions.
* @returns TypedPluginClientBuilder
*/
dependencies(plugins) {
const dependencies = plugins.map((p) => p.def);
const builder = new _PluginClientBuilder({ ...this.def, dependencies });
return builder;
}
/**
* ### `.config()`
*
* Add a configuration that users can provide to tweak plugin client's behavior.
*
* @see TODO: Add docs link
*
* @example
* ```ts
* const myPluginClient = definePluginClient("my-plugin")
* .config({ schema: z.object({ refreshRate: z.number() }) });
*
* const myAgentClient = defineAgentClient("my-agent")
* .plugins([myPluginClient])
* .myPlugin({ refreshRate: 1000 }); // <-- Here
* ```
*
*
* ---
*
* The provided config can then be accessed from `plugin.config` inside atoms and class.
*
* @example
* ```ts
* const pluginClient = definePluginClient("my-plugin")
* .config({ schema: z.object({ refreshRate: z.number() }) });
* .atoms(({ config }) => {
* const refreshRate = config.refreshRate; // <-- Here
* });
* ```
*
*
* ---
* @param config - The config definition.
* @returns TypedPluginClientBuilder
*/
config(config) {
return this.$config(zodObjectWithTelemetry(config));
}
/**
* ### `.$config()`
*
* Define plugin client config from the output of `definePluginClientConfig()`.
*
* @see TODO: Add docs link for `definePluginClientConfig()`
*
* ---
* @param config - The config definition.
* @returns TypedPluginClientBuilder
*/
$config(config) {
const builder = new _PluginClientBuilder({ ...this.def, config });
return builder;
}
/**
* ### `.class()`
*
* TODO
*
* @see TODO: Add docs link
*
* ---
* @param input - The class definition.
* @returns TypedPluginClientBuilder
*/
class(extension) {
const builder = new _PluginClientBuilder({ ...this.def, class: extension });
return builder;
}
/**
* ### `.atoms()`
*
* Add reactive states (atoms) that can then be consumed from various UI frameworks
* (React, Vue, Svelte, etc.) to render the plugin data in real-time on the user interface.
*
* Atoms are powered by [nanostores](https://github.com/nanostores/nanostores).
*
* @see TODO: Add docs link
*
* @example
* ```ts
* import { onMount } from "nanostores";
*
* const pluginClient = definePluginClient("my-plugin")
* .atoms(({ plugin }) => {
* const value = atom(0);
* onMount(() => {
* setInterval(async () => {
* const context = await plugin.context.get();
* value.set(context.value + 1);
* }, 1000);
* });
* return { value };
* });
* ```
*
*
* ---
*
* Then from a React component, you can render the `value` atom:
* ```ts
* import { useStore } from "nanostores/react";
*
* export default function Page() {
* const agent = useAgent("my-agent");
* const value = useStore(agent.myPlugin.atoms.value);
* return <div>{value}</div>;
* };
* ```
*
*
* ---
* @param definition - The atoms definition.
* @returns TypedPluginClientBuilder
*/
atoms(atoms) {
const builder = new _PluginClientBuilder({ ...this.def, atoms });
return builder;
}
};
function definePluginClient(name) {
const defaultDefinition = {
name,
config: zodObjectWithTelemetry({ schema: z.object() }),
dependencies: [],
class: /* @__PURE__ */ __name((..._args) => class {
}, "class"),
atoms: /* @__PURE__ */ __name((..._args) => [], "atoms"),
$serverDef: null
};
return new PluginClientBuilder(defaultDefinition);
}
__name(definePluginClient, "definePluginClient");
// plugins/defaults/generation/client.ts
import { atom, onMount } from "nanostores";
import z2 from "zod";
var generationPluginClient = definePluginClient("generation").config({ schema: z2.object({ enableVoice: z2.boolean().prefault(false) }) }).class(
({ plugin }) => class {
async continue(params) {
return await plugin.server.events.emit({ name: "agent.continue", data: params });
}
async decide(params) {
return await plugin.server.events.emit({ name: "agent.decide", data: params });
}
async say(params) {
return await plugin.server.events.emit({ name: "agent.say", data: params });
}
async interrupt(params) {
return await plugin.server.events.emit({ name: "agent.interrupt", data: params });
}
messages = {
create: /* @__PURE__ */ __name(async (params) => await plugin.server.events.emit({ name: "messages.create", data: params }), "create"),
update: /* @__PURE__ */ __name(async (params) => await plugin.server.events.emit({ name: "messages.update", data: params }), "update"),
get: /* @__PURE__ */ __name(() => plugin.server.context.get().messages, "get")
};
}
).atoms(({ plugin, telemetry }) => [
{
name: "status",
create: /* @__PURE__ */ __name(() => {
const store = atom(null);
onMount(store, () => {
const [error, context] = plugin.server.context.safe.get();
if (error)
telemetry.log.error({
message: "Failed to fetch initial status from context.",
error
});
if (context?.status) store.set(context.status);
const unsubscribe = plugin.server.context.onChange(
(ctx) => ctx.status,
(ctx) => store.set(ctx.status)
);
return () => unsubscribe?.();
});
return { store, refresh: /* @__PURE__ */ __name(async () => void 0, "refresh") };
}, "create")
},
{
name: "messages",
create: /* @__PURE__ */ __name(() => {
const store = atom([]);
onMount(store, () => {
const [error, context] = plugin.server.context.safe.get();
if (error)
telemetry.log.error({
message: "Failed to fetch initial messages from context.",
error
});
if (context?.messages) store.set(context.messages);
const unsubscribe = plugin.server.context.onChange(
(ctx) => ctx.messages,
(ctx) => store.set(ctx.messages)
);
return () => unsubscribe?.();
});
return { store, refresh: /* @__PURE__ */ __name(async () => void 0, "refresh") };
}, "create")
}
]);
// plugins/defaults/memories/client.ts
var memoriesPluginClient = definePluginClient("memories").dependencies([generationPluginClient]).class((_) => class {
});
// plugins/defaults/stores/client.ts
var storesPluginClient = definePluginClient("stores");
// exports/client.ts
var defaults = {
plugins: {
generation: generationPluginClient,
memories: memoriesPluginClient,
stores: storesPluginClient,
*[Symbol.iterator]() {
for (const entry of Object.values(this)) yield entry;
}
}
};
export {
createLifeClient,
defaults,
defineAgentClient,
definePluginClient,
parseAgentClientParam
};
//# sourceMappingURL=client.mjs.map