zooid 0.15.0 → 0.16.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/README.md CHANGED
@@ -42,7 +42,7 @@ zooid dev
42
42
 
43
43
  > **Prerequisite:** `zooid dev` runs Tuwunel inside a container, so you need a container engine installed and running first — either **Docker** (with the daemon started) or **Podman** aliased as `docker`.
44
44
 
45
- `zooid dev` starts a Tuwunel Matrix homeserver in a container, generates the Application Service registration, registers an `admin:admin` user, runs the daemon, and serves the [Zooid web client](https://github.com/zooid-ai/clients).
45
+ `zooid dev` starts a Tuwunel Matrix homeserver in a container, generates the Application Service registration, registers an `admin:admin` user, runs the daemon, and serves the [Zooid web client](https://github.com/zooid-ai/clients). The client's runtime settings come from a generated `/config.json`; its `workforce_space` field is the `matrix.space` alias localpart from `zooid.yaml` (default `dev`). A static deployment sets `workforce_space` in the `config.json` it serves beside the bundle — there is no build-time variable.
46
46
 
47
47
  Open `http://localhost:5173`, log in as `admin` / `admin`, join `#welcome`, and `@`-mention your agent.
48
48
 
package/dist/bin.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  renderTemplate,
21
21
  startAgentSocketServers,
22
22
  startWorkforcePublisher
23
- } from "./chunk-35IKQPC5.js";
23
+ } from "./chunk-HE7DVDJA.js";
24
24
 
25
25
  // src/bin.ts
26
26
  import { resolve as resolve8 } from "path";
@@ -1113,6 +1113,7 @@ async function startDaemon(opts = {}) {
1113
1113
  const registry = buildAcpRegistry(config, {
1114
1114
  approvals,
1115
1115
  onTap: opts.onTap,
1116
+ onLifecycle: opts.onLifecycle,
1116
1117
  agentsDir: opts.agentsDir,
1117
1118
  contextSpawnRegistry,
1118
1119
  daemonSockPaths: contextSockets.paths,
@@ -1658,6 +1659,7 @@ function webStatic(opts) {
1658
1659
  "/config.json",
1659
1660
  (c) => c.json({
1660
1661
  homeserver_url: opts.homeserverUrl,
1662
+ ...opts.workforceSpace ? { workforce_space: opts.workforceSpace } : {},
1661
1663
  ...opts.pushGatewayUrl ? { push_gateway_url: opts.pushGatewayUrl } : {},
1662
1664
  ...opts.vapidPublicKey ? { vapid_public_key: opts.vapidPublicKey } : {}
1663
1665
  })
@@ -1894,6 +1896,12 @@ function wireAgentCapture(opts) {
1894
1896
  const sink = new JsonlSink(opts.paths.agentTap(opts.agent));
1895
1897
  const now = opts.now ?? (() => /* @__PURE__ */ new Date());
1896
1898
  const pending = [];
1899
+ const counters = {
1900
+ closeAttempts: 0,
1901
+ closeSuccesses: 0,
1902
+ sessionsReclaimed: 0,
1903
+ recoveries: { cached: 0, resume: 0, load: 0, new: 0 }
1904
+ };
1897
1905
  const onTap = (event) => {
1898
1906
  if (event.kind === "session_update") {
1899
1907
  const variant = event.update.sessionUpdate ?? "unknown";
@@ -1915,6 +1923,25 @@ function wireAgentCapture(opts) {
1915
1923
  };
1916
1924
  return {
1917
1925
  onTap,
1926
+ onLifecycle: (event) => {
1927
+ if (event.reason && event.outcome !== "unsupported") counters.closeAttempts++;
1928
+ if (event.reason && event.outcome === "closed") {
1929
+ counters.closeSuccesses++;
1930
+ counters.sessionsReclaimed++;
1931
+ }
1932
+ if (event.outcome === "recovered" && event.recoveryMethod) counters.recoveries[event.recoveryMethod]++;
1933
+ pending.push(sink.write({
1934
+ ts: now().toISOString(),
1935
+ agent: opts.agent,
1936
+ kind: "session_lifecycle",
1937
+ session_key: event.sessionKey,
1938
+ session_id: event.sessionId,
1939
+ reason: event.reason,
1940
+ outcome: event.outcome,
1941
+ recovery_method: event.recoveryMethod
1942
+ }));
1943
+ },
1944
+ readLifecycleCounters: () => ({ ...counters, recoveries: { ...counters.recoveries } }),
1918
1945
  close: async () => {
1919
1946
  await Promise.all(pending);
1920
1947
  await sink.close();
@@ -2080,6 +2107,7 @@ async function runDev(flags) {
2080
2107
  // the web client without needing an invite.
2081
2108
  publicWorkforceSpace: true,
2082
2109
  onTap: (agentName, event) => captures[agentName]?.onTap(event),
2110
+ onLifecycle: (agentName, event) => captures[agentName]?.onLifecycle(event),
2083
2111
  prepullLog: (line) => {
2084
2112
  t.output = line.replace(/^\[zooid\]\s+/, "").trim();
2085
2113
  }
@@ -2116,6 +2144,8 @@ Pass an explicit path: --watch-web=/path/to/zooid-clients/packages/web`
2116
2144
  const app = webStatic({
2117
2145
  webRoot,
2118
2146
  homeserverUrl: homeserver,
2147
+ // Same default as the daemon's space provisioning (start-daemon.ts).
2148
+ workforceSpace: matrix.transport.space ?? "dev",
2119
2149
  ...ctx.daemon?.vapidPublicKey ? {
2120
2150
  // Tuwunel runs in a container; `localhost` here would
2121
2151
  // resolve to the container, not the daemon. Same shorthand
@@ -2778,6 +2808,22 @@ async function dumpByTurn(dayDir, turnId, writer) {
2778
2808
  // src/commands/start.ts
2779
2809
  import { randomBytes as randomBytes2 } from "crypto";
2780
2810
  import { resolve as resolve6 } from "path";
2811
+
2812
+ // src/observability/lifecycle-log.ts
2813
+ function formatLifecycleLine(agentName, event) {
2814
+ if (event.outcome === "unsupported") return null;
2815
+ if (event.recoveryMethod === "cached") return null;
2816
+ const what = event.outcome === "recovered" ? `recovered (${event.recoveryMethod ?? "unknown"})` : `${event.reason ?? "close"} \u2192 ${event.outcome}`;
2817
+ return `[lifecycle] ${agentName} key=${event.sessionKey} session=${event.sessionId} ${what}`;
2818
+ }
2819
+ function logLifecycle(agentName, event, out = console) {
2820
+ const line = formatLifecycleLine(agentName, event);
2821
+ if (!line) return;
2822
+ if (event.outcome === "failed") out.error(line);
2823
+ else out.log(line);
2824
+ }
2825
+
2826
+ // src/commands/start.ts
2781
2827
  async function runStart(flags) {
2782
2828
  if (flags.printToken) {
2783
2829
  process.stdout.write(`${randomBytes2(32).toString("hex")}
@@ -2789,7 +2835,8 @@ async function runStart(flags) {
2789
2835
  const handle = await startDaemon({
2790
2836
  cliFlags: flags,
2791
2837
  installSignalHandlers: true,
2792
- agentsDir: layout.agentsDir
2838
+ agentsDir: layout.agentsDir,
2839
+ onLifecycle: (agentName, event) => logLifecycle(agentName, event)
2793
2840
  });
2794
2841
  console.log(`zooid listening on http://localhost:${handle.port}`);
2795
2842
  for (const name of handle.agentNames) console.log(` agent: ${name}`);