zooid 0.14.1 → 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 +1 -1
- package/dist/bin.js +93 -25
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-R5S26T7B.js → chunk-HE7DVDJA.js} +1545 -816
- package/dist/chunk-HE7DVDJA.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/package.json +10 -10
- package/src/build-registry.context.test.ts +1 -0
- package/src/build-registry.test.ts +1 -0
- package/src/build-registry.ts +3 -0
- package/src/build-registry.zod043.test.ts +1 -0
- package/src/build-registry.zod044.test.ts +1 -0
- package/src/commands/dev.ts +3 -0
- package/src/commands/start.ts +2 -0
- package/src/daemon/start-daemon.ts +6 -0
- package/src/daemon/trigger-runner.mxid.test.ts +145 -0
- package/src/daemon/trigger-runner.test.ts +3 -4
- package/src/daemon/trigger-runner.ts +45 -7
- package/src/daemon/trigger-scheduler.ts +12 -7
- package/src/daemon/webhook-routes.ts +3 -7
- package/src/observability/capture-agent.ts +35 -1
- package/src/observability/lifecycle-log.test.ts +48 -0
- package/src/observability/lifecycle-log.ts +29 -0
- package/src/observability/observability.integration.test.ts +22 -0
- package/src/web/static.test.ts +14 -0
- package/src/web/static.ts +3 -0
- package/dist/chunk-R5S26T7B.js.map +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SessionLifecycleEvent } from '@zooid/core'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One grep-able line per ACP session lifecycle event, for `zooid start`'s
|
|
5
|
+
* stdout/stderr ([ZOD089]). Carries identities and outcomes only — never
|
|
6
|
+
* prompt or transcript content. Skipped: `unsupported` (the ACP client
|
|
7
|
+
* already warns once per agent) and `cached` recoveries, which fire on every
|
|
8
|
+
* prompt to a live session and would add a line per message.
|
|
9
|
+
*/
|
|
10
|
+
export function formatLifecycleLine(agentName: string, event: SessionLifecycleEvent): string | null {
|
|
11
|
+
if (event.outcome === 'unsupported') return null
|
|
12
|
+
if (event.recoveryMethod === 'cached') return null
|
|
13
|
+
const what =
|
|
14
|
+
event.outcome === 'recovered'
|
|
15
|
+
? `recovered (${event.recoveryMethod ?? 'unknown'})`
|
|
16
|
+
: `${event.reason ?? 'close'} → ${event.outcome}`
|
|
17
|
+
return `[lifecycle] ${agentName} key=${event.sessionKey} session=${event.sessionId} ${what}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function logLifecycle(
|
|
21
|
+
agentName: string,
|
|
22
|
+
event: SessionLifecycleEvent,
|
|
23
|
+
out: Pick<Console, 'log' | 'error'> = console,
|
|
24
|
+
): void {
|
|
25
|
+
const line = formatLifecycleLine(agentName, event)
|
|
26
|
+
if (!line) return
|
|
27
|
+
if (event.outcome === 'failed') out.error(line)
|
|
28
|
+
else out.log(line)
|
|
29
|
+
}
|
|
@@ -16,6 +16,28 @@ describe('agent capture end-to-end', () => {
|
|
|
16
16
|
rmSync(dataDir, { recursive: true, force: true })
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
+
it('captures lifecycle identity and counts only successful reclamation', async () => {
|
|
20
|
+
const now = new Date('2026-05-06T10:00:00Z')
|
|
21
|
+
const paths = resolveLogPaths({ dataDir, now })
|
|
22
|
+
await ensureDayFolder(paths)
|
|
23
|
+
const cap = wireAgentCapture({ agent: 'docs', paths, verbosity: 'default', matrixContext: () => null, now: () => now })
|
|
24
|
+
cap.onLifecycle({ agentId: 'docs', sessionKey: 'root', sessionId: 's1', outcome: 'recovered', recoveryMethod: 'new' })
|
|
25
|
+
cap.onLifecycle({ agentId: 'docs', sessionKey: 'root', sessionId: 's1', reason: 'idle', outcome: 'closed' })
|
|
26
|
+
cap.onLifecycle({ agentId: 'docs', sessionKey: 'root', sessionId: 's1', outcome: 'recovered', recoveryMethod: 'resume' })
|
|
27
|
+
cap.onLifecycle({ agentId: 'docs', sessionKey: 'root', sessionId: 's1', reason: 'clear', outcome: 'failed' })
|
|
28
|
+
expect(cap.readLifecycleCounters()).toEqual({
|
|
29
|
+
closeAttempts: 2,
|
|
30
|
+
closeSuccesses: 1,
|
|
31
|
+
sessionsReclaimed: 1,
|
|
32
|
+
recoveries: { cached: 0, resume: 1, load: 0, new: 1 },
|
|
33
|
+
})
|
|
34
|
+
await cap.close()
|
|
35
|
+
const rows = (await readFile(paths.agentTap('docs'), 'utf8')).trim().split('\n').map((line) => JSON.parse(line))
|
|
36
|
+
expect(rows).toHaveLength(4)
|
|
37
|
+
expect(rows[1]).toMatchObject({ kind: 'session_lifecycle', session_key: 'root', session_id: 's1', reason: 'idle', outcome: 'closed' })
|
|
38
|
+
expect(rows[1]).not.toHaveProperty('prompt_text')
|
|
39
|
+
})
|
|
40
|
+
|
|
19
41
|
it('writes one JSONL line per tap event with envelope + turn correlation', async () => {
|
|
20
42
|
const now = new Date('2026-05-06T10:00:00Z')
|
|
21
43
|
const paths = resolveLogPaths({ dataDir, now })
|
package/src/web/static.test.ts
CHANGED
|
@@ -64,6 +64,20 @@ describe('webStatic', () => {
|
|
|
64
64
|
})
|
|
65
65
|
})
|
|
66
66
|
|
|
67
|
+
it('serves workforce_space when configured, default or custom', async () => {
|
|
68
|
+
for (const space of ['dev', 'hq']) {
|
|
69
|
+
const app = webStatic({
|
|
70
|
+
webRoot: dir,
|
|
71
|
+
homeserverUrl: 'http://localhost:8448',
|
|
72
|
+
workforceSpace: space,
|
|
73
|
+
})
|
|
74
|
+
expect(await (await app.request('/config.json')).json()).toEqual({
|
|
75
|
+
homeserver_url: 'http://localhost:8448',
|
|
76
|
+
workforce_space: space,
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
67
81
|
it('omits both when unconfigured, so the client falls back instead of half-subscribing', async () => {
|
|
68
82
|
const app = webStatic({ webRoot: dir, homeserverUrl: 'http://localhost:8448' })
|
|
69
83
|
expect(await (await app.request('/config.json')).json()).toEqual({
|
package/src/web/static.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { Hono } from 'hono'
|
|
|
5
5
|
export interface WebStaticOpts {
|
|
6
6
|
webRoot: string
|
|
7
7
|
homeserverUrl: string
|
|
8
|
+
/** Workforce space alias localpart, copied from `matrix.space`. */
|
|
9
|
+
workforceSpace?: string
|
|
8
10
|
pushGatewayUrl?: string
|
|
9
11
|
vapidPublicKey?: string
|
|
10
12
|
}
|
|
@@ -33,6 +35,7 @@ export function webStatic(opts: WebStaticOpts): Hono {
|
|
|
33
35
|
app.get('/config.json', (c) =>
|
|
34
36
|
c.json({
|
|
35
37
|
homeserver_url: opts.homeserverUrl,
|
|
38
|
+
...(opts.workforceSpace ? { workforce_space: opts.workforceSpace } : {}),
|
|
36
39
|
...(opts.pushGatewayUrl ? { push_gateway_url: opts.pushGatewayUrl } : {}),
|
|
37
40
|
...(opts.vapidPublicKey ? { vapid_public_key: opts.vapidPublicKey } : {}),
|
|
38
41
|
}),
|