bazilion 0.0.0 → 0.1.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 +53 -152
- package/dist/cli.js +2730 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon.js +5888 -0
- package/dist/daemon.js.map +1 -0
- package/dist/worker.js +1830 -0
- package/dist/worker.js.map +1 -0
- package/package.json +57 -28
- package/.understand-anything/.understandignore +0 -25
- package/.understand-anything/fingerprints.json +0 -14267
- package/.understand-anything/knowledge-graph.json +0 -18128
- package/.understand-anything/meta.json +0 -6
- package/CLAUDE.md +0 -164
- package/apps/cli/package.json +0 -21
- package/apps/cli/src/auth-file.ts +0 -18
- package/apps/cli/src/client.ts +0 -37
- package/apps/cli/src/columnize.ts +0 -32
- package/apps/cli/src/commands/agent.ts +0 -574
- package/apps/cli/src/commands/auth.ts +0 -110
- package/apps/cli/src/commands/backup.ts +0 -135
- package/apps/cli/src/commands/completion.ts +0 -155
- package/apps/cli/src/commands/config.ts +0 -95
- package/apps/cli/src/commands/doctor.ts +0 -131
- package/apps/cli/src/commands/group.ts +0 -132
- package/apps/cli/src/commands/inbox.ts +0 -82
- package/apps/cli/src/commands/login.ts +0 -73
- package/apps/cli/src/commands/memory.ts +0 -106
- package/apps/cli/src/commands/profile.ts +0 -259
- package/apps/cli/src/commands/provider.ts +0 -170
- package/apps/cli/src/commands/send.ts +0 -24
- package/apps/cli/src/commands/serve.ts +0 -89
- package/apps/cli/src/commands/skill.ts +0 -120
- package/apps/cli/src/commands/token.ts +0 -148
- package/apps/cli/src/commands/trigger.ts +0 -129
- package/apps/cli/src/commands/uninstall.ts +0 -156
- package/apps/cli/src/index.ts +0 -196
- package/apps/cli/src/paths.ts +0 -12
- package/apps/cli/test/agent.test.ts +0 -213
- package/apps/cli/test/backup.test.ts +0 -95
- package/apps/cli/test/chat.test.ts +0 -539
- package/apps/cli/test/columnize.test.ts +0 -29
- package/apps/cli/test/completion.test.ts +0 -45
- package/apps/cli/test/config-page.test.ts +0 -151
- package/apps/cli/test/group.test.ts +0 -74
- package/apps/cli/test/helpers.ts +0 -70
- package/apps/cli/test/inbox-autodeliver.test.ts +0 -143
- package/apps/cli/test/inbox.test.ts +0 -147
- package/apps/cli/test/memory.test.ts +0 -69
- package/apps/cli/test/profile.test.ts +0 -110
- package/apps/cli/test/send.test.ts +0 -30
- package/apps/cli/test/server-fixture.ts +0 -212
- package/apps/cli/test/session-head.test.ts +0 -45
- package/apps/cli/test/skill.test.ts +0 -128
- package/apps/cli/test/token.test.ts +0 -109
- package/apps/cli/test/trigger.test.ts +0 -245
- package/apps/cli/tsconfig.json +0 -4
- package/apps/daemon/package.json +0 -31
- package/apps/daemon/src/app.ts +0 -41
- package/apps/daemon/src/core/agent/archive.ts +0 -8
- package/apps/daemon/src/core/agent/delete.ts +0 -30
- package/apps/daemon/src/core/agent/resolve.ts +0 -31
- package/apps/daemon/src/core/agent/spawn.ts +0 -95
- package/apps/daemon/src/core/agent/unarchive.ts +0 -11
- package/apps/daemon/src/core/availableModels.ts +0 -58
- package/apps/daemon/src/core/db/client.ts +0 -113
- package/apps/daemon/src/core/db/migrate.ts +0 -41
- package/apps/daemon/src/core/db/migrations/0001_init.sql +0 -179
- package/apps/daemon/src/core/group/delete.ts +0 -21
- package/apps/daemon/src/core/group/register.ts +0 -68
- package/apps/daemon/src/core/index.ts +0 -71
- package/apps/daemon/src/core/paths.ts +0 -56
- package/apps/daemon/src/core/profile/create.ts +0 -78
- package/apps/daemon/src/core/profile/delete.ts +0 -26
- package/apps/daemon/src/core/profile/identity.ts +0 -70
- package/apps/daemon/src/core/profile/load.ts +0 -45
- package/apps/daemon/src/core/profile/seed.ts +0 -74
- package/apps/daemon/src/core/profile/templates.ts +0 -60
- package/apps/daemon/src/core/profile/update.ts +0 -57
- package/apps/daemon/src/core/profile/validate.ts +0 -9
- package/apps/daemon/src/core/repos/agents.ts +0 -202
- package/apps/daemon/src/core/repos/config.ts +0 -78
- package/apps/daemon/src/core/repos/groups.ts +0 -55
- package/apps/daemon/src/core/repos/messages.ts +0 -127
- package/apps/daemon/src/core/repos/profiles.ts +0 -83
- package/apps/daemon/src/core/repos/providerModels.ts +0 -58
- package/apps/daemon/src/core/repos/providerState.ts +0 -37
- package/apps/daemon/src/core/repos/secrets.ts +0 -145
- package/apps/daemon/src/core/repos/skillMeta.ts +0 -49
- package/apps/daemon/src/core/repos/triggers.ts +0 -101
- package/apps/daemon/src/core/repos/webTokens.ts +0 -87
- package/apps/daemon/src/core/secrets.ts +0 -65
- package/apps/daemon/src/core/services.ts +0 -264
- package/apps/daemon/src/core/skills/discover.ts +0 -28
- package/apps/daemon/src/core/skills/import.ts +0 -136
- package/apps/daemon/src/core/skills/parse.ts +0 -52
- package/apps/daemon/src/core/skills/resolve.ts +0 -50
- package/apps/daemon/src/index.ts +0 -45
- package/apps/daemon/src/lib/agent-cancel.ts +0 -48
- package/apps/daemon/src/lib/agent-id.ts +0 -13
- package/apps/daemon/src/lib/agent-turn.ts +0 -56
- package/apps/daemon/src/lib/api-key.ts +0 -56
- package/apps/daemon/src/lib/auth.ts +0 -41
- package/apps/daemon/src/lib/cron.ts +0 -93
- package/apps/daemon/src/lib/ctx.ts +0 -80
- package/apps/daemon/src/lib/messaging-host.ts +0 -34
- package/apps/daemon/src/lib/middleware-auth.ts +0 -52
- package/apps/daemon/src/lib/scheduler.ts +0 -294
- package/apps/daemon/src/routes/agents.ts +0 -772
- package/apps/daemon/src/routes/auth-login.ts +0 -193
- package/apps/daemon/src/routes/config.ts +0 -267
- package/apps/daemon/src/routes/groups.ts +0 -133
- package/apps/daemon/src/routes/messages.ts +0 -29
- package/apps/daemon/src/routes/misc.ts +0 -239
- package/apps/daemon/src/routes/profiles.ts +0 -197
- package/apps/daemon/src/routes/skills.ts +0 -123
- package/apps/daemon/src/routes/triggers.ts +0 -29
- package/apps/daemon/src/runtime/auth/openai-codex.ts +0 -121
- package/apps/daemon/src/runtime/auto-reply/heartbeat.ts +0 -31
- package/apps/daemon/src/runtime/index.ts +0 -77
- package/apps/daemon/src/runtime/memory/files.ts +0 -103
- package/apps/daemon/src/runtime/memory/qmd.ts +0 -152
- package/apps/daemon/src/runtime/memory/types.ts +0 -16
- package/apps/daemon/src/runtime/pi/events.ts +0 -173
- package/apps/daemon/src/runtime/pi/session.ts +0 -536
- package/apps/daemon/src/runtime/pi/tools.ts +0 -85
- package/apps/daemon/src/runtime/providers/catalog.ts +0 -145
- package/apps/daemon/src/runtime/providers/pi-adapter.ts +0 -272
- package/apps/daemon/src/runtime/providers/registry.ts +0 -374
- package/apps/daemon/src/runtime/providers/retry.ts +0 -176
- package/apps/daemon/src/runtime/providers/types.ts +0 -33
- package/apps/daemon/src/runtime/session/prompt.ts +0 -83
- package/apps/daemon/src/runtime/tools/bootstrap.ts +0 -22
- package/apps/daemon/src/runtime/tools/home.ts +0 -114
- package/apps/daemon/src/runtime/tools/memory.ts +0 -81
- package/apps/daemon/src/runtime/tools/messaging.ts +0 -127
- package/apps/daemon/src/runtime/tools/registry.ts +0 -29
- package/apps/daemon/src/runtime/tools/types.ts +0 -13
- package/apps/daemon/src/runtime/tools/web-extract.ts +0 -110
- package/apps/daemon/src/runtime/tools/web-ssrf.ts +0 -245
- package/apps/daemon/src/runtime/tools/web.ts +0 -273
- package/apps/daemon/src/runtime/worker/entry.ts +0 -221
- package/apps/daemon/src/runtime/worker/ipc-protocol.ts +0 -75
- package/apps/daemon/src/runtime/worker/spawn.ts +0 -249
- package/apps/daemon/test/core/agents.test.ts +0 -319
- package/apps/daemon/test/core/available-models.test.ts +0 -63
- package/apps/daemon/test/core/config.test.ts +0 -99
- package/apps/daemon/test/core/groups.test.ts +0 -63
- package/apps/daemon/test/core/helpers.ts +0 -50
- package/apps/daemon/test/core/identity.test.ts +0 -85
- package/apps/daemon/test/core/migrations.test.ts +0 -83
- package/apps/daemon/test/core/profiles.test.ts +0 -182
- package/apps/daemon/test/core/provider-models.test.ts +0 -57
- package/apps/daemon/test/core/provider-state.test.ts +0 -36
- package/apps/daemon/test/core/skill-meta.test.ts +0 -45
- package/apps/daemon/test/core/skills.test.ts +0 -271
- package/apps/daemon/test/core/triggers.test.ts +0 -182
- package/apps/daemon/test/core/web-tokens.test.ts +0 -79
- package/apps/daemon/test/cron.test.ts +0 -90
- package/apps/daemon/test/runtime/heartbeat.test.ts +0 -34
- package/apps/daemon/test/runtime/memory-qmd.test.ts +0 -97
- package/apps/daemon/test/runtime/memory.test.ts +0 -78
- package/apps/daemon/test/runtime/messaging.test.ts +0 -213
- package/apps/daemon/test/runtime/mock-server.ts +0 -65
- package/apps/daemon/test/runtime/openai-codex-auth.test.ts +0 -116
- package/apps/daemon/test/runtime/providers.test.ts +0 -306
- package/apps/daemon/test/runtime/retry.test.ts +0 -191
- package/apps/daemon/test/runtime/session-head.test.ts +0 -90
- package/apps/daemon/test/runtime/tools-home.test.ts +0 -102
- package/apps/daemon/test/runtime/tools-web.test.ts +0 -206
- package/apps/daemon/tsconfig.json +0 -4
- package/apps/mobile/README.md +0 -60
- package/apps/mobile/app/_layout.tsx +0 -58
- package/apps/mobile/app/agents/[id]/chat.tsx +0 -486
- package/apps/mobile/app/agents/[id]/index.tsx +0 -166
- package/apps/mobile/app/agents/index.tsx +0 -212
- package/apps/mobile/app/index.tsx +0 -21
- package/apps/mobile/app/pair.tsx +0 -226
- package/apps/mobile/app/settings.tsx +0 -419
- package/apps/mobile/app.json +0 -49
- package/apps/mobile/assets/adaptive-icon.png +0 -0
- package/apps/mobile/assets/favicon.png +0 -0
- package/apps/mobile/assets/icon.png +0 -0
- package/apps/mobile/assets/splash-icon.png +0 -0
- package/apps/mobile/babel.config.js +0 -6
- package/apps/mobile/metro.config.js +0 -28
- package/apps/mobile/package.json +0 -44
- package/apps/mobile/src/auth.ts +0 -66
- package/apps/mobile/src/pair-url.ts +0 -48
- package/apps/mobile/src/theme-context.tsx +0 -88
- package/apps/mobile/src/theme.ts +0 -135
- package/apps/mobile/test/pair-url.test.ts +0 -46
- package/apps/mobile/tsconfig.json +0 -23
- package/apps/web/components.json +0 -25
- package/apps/web/package.json +0 -39
- package/apps/web/public/baziu.svg +0 -8
- package/apps/web/src/components/AgentTabs.tsx +0 -45
- package/apps/web/src/components/BaziuLogo.tsx +0 -21
- package/apps/web/src/components/ChatPane.tsx +0 -1033
- package/apps/web/src/components/ConfigTabs.tsx +0 -29
- package/apps/web/src/components/CopyButton.tsx +0 -68
- package/apps/web/src/components/CreateGroupDialog.tsx +0 -127
- package/apps/web/src/components/FieldRow.tsx +0 -94
- package/apps/web/src/components/Footer.tsx +0 -10
- package/apps/web/src/components/PawIcon.tsx +0 -15
- package/apps/web/src/components/Sidebar.tsx +0 -287
- package/apps/web/src/components/SpawnDialog.tsx +0 -129
- package/apps/web/src/components/ThemeToggle.tsx +0 -75
- package/apps/web/src/components/TopNav.tsx +0 -34
- package/apps/web/src/components/ui/button.tsx +0 -67
- package/apps/web/src/components/ui/card.tsx +0 -103
- package/apps/web/src/components/ui/checkbox.tsx +0 -31
- package/apps/web/src/components/ui/dialog.tsx +0 -168
- package/apps/web/src/components/ui/input.tsx +0 -19
- package/apps/web/src/components/ui/label.tsx +0 -22
- package/apps/web/src/components/ui/radio-group.tsx +0 -44
- package/apps/web/src/components/ui/select.tsx +0 -192
- package/apps/web/src/components/ui/separator.tsx +0 -26
- package/apps/web/src/components/ui/table.tsx +0 -116
- package/apps/web/src/components/ui/tabs.tsx +0 -88
- package/apps/web/src/components/ui/textarea.tsx +0 -18
- package/apps/web/src/lib/auth.ts +0 -50
- package/apps/web/src/lib/daemon-client.ts +0 -34
- package/apps/web/src/lib/md.ts +0 -45
- package/apps/web/src/lib/utils.ts +0 -6
- package/apps/web/src/lib/wire-constants.ts +0 -27
- package/apps/web/src/routeTree.gen.ts +0 -408
- package/apps/web/src/router.tsx +0 -20
- package/apps/web/src/routes/__root.tsx +0 -123
- package/apps/web/src/routes/agents/$id/inbox.tsx +0 -207
- package/apps/web/src/routes/agents/$id/index.tsx +0 -527
- package/apps/web/src/routes/agents/$id/triggers.tsx +0 -239
- package/apps/web/src/routes/agents/index.tsx +0 -265
- package/apps/web/src/routes/api/$.ts +0 -88
- package/apps/web/src/routes/config/index.tsx +0 -315
- package/apps/web/src/routes/config/services.tsx +0 -49
- package/apps/web/src/routes/config/tokens.tsx +0 -192
- package/apps/web/src/routes/groups/$id/index.tsx +0 -153
- package/apps/web/src/routes/groups/$id/memory.tsx +0 -321
- package/apps/web/src/routes/groups/index.tsx +0 -191
- package/apps/web/src/routes/index.tsx +0 -133
- package/apps/web/src/routes/login.tsx +0 -63
- package/apps/web/src/routes/profiles/$id.tsx +0 -549
- package/apps/web/src/routes/profiles/index.tsx +0 -458
- package/apps/web/src/routes/skills/index.tsx +0 -297
- package/apps/web/src/routes/welcome.tsx +0 -61
- package/apps/web/src/styles.css +0 -449
- package/apps/web/tsconfig.json +0 -25
- package/apps/web/vite.config.ts +0 -25
- package/biome.json +0 -23
- package/docs/agent-engine.md +0 -219
- package/docs/architecture.md +0 -627
- package/docs/backlog/README.md +0 -42
- package/docs/backlog/draft/BAZ-001-a2a-federation-spike.md +0 -125
- package/docs/openclaw-reference.md +0 -210
- package/packages/api-types/package.json +0 -11
- package/packages/api-types/src/entities.ts +0 -146
- package/packages/api-types/src/events.ts +0 -55
- package/packages/api-types/src/index.ts +0 -488
- package/packages/api-types/src/memory.ts +0 -15
- package/packages/client/package.json +0 -13
- package/packages/client/src/index.ts +0 -117
- package/pnpm-workspace.yaml +0 -3
- package/tsconfig.base.json +0 -23
- package/tsconfig.json +0 -11
- package/vitest.config.ts +0 -22
|
@@ -1,772 +0,0 @@
|
|
|
1
|
-
// /api/agents/* — agent CRUD + lifecycle + sub-resources (group, skills,
|
|
2
|
-
// triggers, messages, sessions, chat). Memory is per-group and lives on
|
|
3
|
-
// the groups router.
|
|
4
|
-
//
|
|
5
|
-
// Sub-resources are inlined here rather than split across files because they
|
|
6
|
-
// all share `/api/agents/:id/...` and benefit from being adjacent — e.g. the
|
|
7
|
-
// chat streaming endpoint and chat/compact next to each other.
|
|
8
|
-
|
|
9
|
-
import { existsSync, readdirSync, readFileSync, rmSync } from 'node:fs'
|
|
10
|
-
import { join } from 'node:path'
|
|
11
|
-
import {
|
|
12
|
-
type AttachSkillRequest,
|
|
13
|
-
type ChatCompactRequest,
|
|
14
|
-
type ChatCompactResponse,
|
|
15
|
-
type ChatContextResponse,
|
|
16
|
-
type ContextFileEntry,
|
|
17
|
-
type ContextGroupEntry,
|
|
18
|
-
type ContextSkillEntry,
|
|
19
|
-
type ContextToolEntry,
|
|
20
|
-
type CreateTriggerRequest,
|
|
21
|
-
type ListInboxResponse,
|
|
22
|
-
type MoveAgentRequest,
|
|
23
|
-
REASONING_LEVELS,
|
|
24
|
-
type ReasoningLevel,
|
|
25
|
-
type ResolvedSkillsResponse,
|
|
26
|
-
type SendMessageRequest,
|
|
27
|
-
type SessionHeadResponse,
|
|
28
|
-
type SpawnAgentRequest,
|
|
29
|
-
type TruncateChatRequest,
|
|
30
|
-
type TruncateChatResponse,
|
|
31
|
-
} from '@bazilion/api-types'
|
|
32
|
-
import { Hono } from 'hono'
|
|
33
|
-
import {
|
|
34
|
-
agentRepo,
|
|
35
|
-
archiveAgent,
|
|
36
|
-
deleteAgent,
|
|
37
|
-
discoverSkills,
|
|
38
|
-
groupRepo,
|
|
39
|
-
mergeSecretsIntoEnv,
|
|
40
|
-
messageRepo,
|
|
41
|
-
providerStateRepo,
|
|
42
|
-
resolveAgent,
|
|
43
|
-
resolveAgentSkills,
|
|
44
|
-
skillMetaRepo,
|
|
45
|
-
spawnAgent,
|
|
46
|
-
triggerRepo,
|
|
47
|
-
unarchiveAgent,
|
|
48
|
-
} from '../core/index.ts'
|
|
49
|
-
import { cancelAgent } from '../lib/agent-cancel.ts'
|
|
50
|
-
import { resolveAgentIdParam } from '../lib/agent-id.ts'
|
|
51
|
-
import { runAgentTurn } from '../lib/agent-turn.ts'
|
|
52
|
-
import { resolveAgentApiKey } from '../lib/api-key.ts'
|
|
53
|
-
import { validateCron } from '../lib/cron.ts'
|
|
54
|
-
import { getCtx } from '../lib/ctx.ts'
|
|
55
|
-
import { createDbMessagingHost } from '../lib/messaging-host.ts'
|
|
56
|
-
import {
|
|
57
|
-
buildSystemPrompt,
|
|
58
|
-
createBazilionSession,
|
|
59
|
-
loadInitialMessages,
|
|
60
|
-
loadSessionHead,
|
|
61
|
-
piMessagesToProviderView,
|
|
62
|
-
qmdBackend,
|
|
63
|
-
} from '../runtime/index.ts'
|
|
64
|
-
|
|
65
|
-
export const agentsRouter = new Hono()
|
|
66
|
-
|
|
67
|
-
// ─── CRUD + lifecycle ────────────────────────────────────────────────────
|
|
68
|
-
|
|
69
|
-
agentsRouter.get('/', (c) => {
|
|
70
|
-
const includeArchived = c.req.query('includeArchived') === 'true'
|
|
71
|
-
const { db, paths, authToken } = getCtx()
|
|
72
|
-
return c.json(agentRepo.list(db, { includeArchived }))
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
agentsRouter.post('/', async (c) => {
|
|
76
|
-
const raw = (await c.req.json().catch(() => null)) as
|
|
77
|
-
| (Record<string, unknown> & Partial<SpawnAgentRequest>)
|
|
78
|
-
| null
|
|
79
|
-
if (!raw) return c.json({ error: 'invalid JSON body' }, 400)
|
|
80
|
-
const profileId =
|
|
81
|
-
typeof raw.profileId === 'string'
|
|
82
|
-
? raw.profileId
|
|
83
|
-
: typeof raw.profile === 'string'
|
|
84
|
-
? raw.profile
|
|
85
|
-
: ''
|
|
86
|
-
if (!profileId) return c.json({ error: 'profileId is required' }, 400)
|
|
87
|
-
const name = typeof raw.name === 'string' && raw.name ? raw.name : undefined
|
|
88
|
-
const model =
|
|
89
|
-
typeof raw.model === 'string' && raw.model
|
|
90
|
-
? raw.model
|
|
91
|
-
: typeof raw.modelOverride === 'string' && raw.modelOverride
|
|
92
|
-
? raw.modelOverride
|
|
93
|
-
: undefined
|
|
94
|
-
const groupId =
|
|
95
|
-
typeof raw.groupId === 'string' && raw.groupId
|
|
96
|
-
? raw.groupId
|
|
97
|
-
: typeof raw.group === 'string' && raw.group
|
|
98
|
-
? (raw.group as string)
|
|
99
|
-
: undefined
|
|
100
|
-
let reasoningLevel: ReasoningLevel | undefined
|
|
101
|
-
if (typeof raw.reasoningLevel === 'string') {
|
|
102
|
-
if (!REASONING_LEVELS.includes(raw.reasoningLevel as ReasoningLevel)) {
|
|
103
|
-
return c.json({ error: `invalid reasoningLevel: ${raw.reasoningLevel}` }, 400)
|
|
104
|
-
}
|
|
105
|
-
reasoningLevel = raw.reasoningLevel as ReasoningLevel
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const { db, paths, authToken } = getCtx()
|
|
109
|
-
try {
|
|
110
|
-
const agent = spawnAgent(db, paths, {
|
|
111
|
-
profileId,
|
|
112
|
-
name,
|
|
113
|
-
modelOverride: model,
|
|
114
|
-
reasoningLevel,
|
|
115
|
-
groupId,
|
|
116
|
-
})
|
|
117
|
-
return c.json(agent, 201)
|
|
118
|
-
} catch (err) {
|
|
119
|
-
return c.json({ error: (err as Error).message }, 400)
|
|
120
|
-
}
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
agentsRouter.get('/:id', (c) => {
|
|
124
|
-
const { db, paths, authToken } = getCtx()
|
|
125
|
-
const id = resolveAgentIdParam(db, c.req.param('id'))
|
|
126
|
-
try {
|
|
127
|
-
return c.json(resolveAgent(db, paths, id))
|
|
128
|
-
} catch (err) {
|
|
129
|
-
return c.json({ error: (err as Error).message }, 404)
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
agentsRouter.patch('/:id', async (c) => {
|
|
134
|
-
const body = (await c.req.json().catch(() => null)) as Record<string, unknown> | null
|
|
135
|
-
if (!body) return c.json({ error: 'invalid JSON body' }, 400)
|
|
136
|
-
const { db, paths, authToken } = getCtx()
|
|
137
|
-
const resolvedId = agentRepo.resolveId(db, c.req.param('id'))
|
|
138
|
-
if (!resolvedId) return c.json({ error: `agent not found: ${c.req.param('id')}` }, 404)
|
|
139
|
-
|
|
140
|
-
if (body.name !== undefined) {
|
|
141
|
-
if (typeof body.name !== 'string') return c.json({ error: 'name must be a string' }, 400)
|
|
142
|
-
const trimmed = body.name.trim()
|
|
143
|
-
if (!trimmed) return c.json({ error: 'name cannot be empty' }, 400)
|
|
144
|
-
agentRepo.setName(db, resolvedId, trimmed)
|
|
145
|
-
}
|
|
146
|
-
if (body.reasoningLevel !== undefined) {
|
|
147
|
-
if (
|
|
148
|
-
typeof body.reasoningLevel !== 'string' ||
|
|
149
|
-
!REASONING_LEVELS.includes(body.reasoningLevel as ReasoningLevel)
|
|
150
|
-
) {
|
|
151
|
-
return c.json({ error: `invalid reasoningLevel: ${body.reasoningLevel}` }, 400)
|
|
152
|
-
}
|
|
153
|
-
agentRepo.setReasoningLevel(db, resolvedId, body.reasoningLevel as ReasoningLevel)
|
|
154
|
-
}
|
|
155
|
-
if (body.modelOverride !== undefined) {
|
|
156
|
-
if (body.modelOverride !== null && typeof body.modelOverride !== 'string') {
|
|
157
|
-
return c.json({ error: 'modelOverride must be a string or null' }, 400)
|
|
158
|
-
}
|
|
159
|
-
const value = body.modelOverride === '' ? null : (body.modelOverride as string | null)
|
|
160
|
-
agentRepo.setModelOverride(db, resolvedId, value)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const agent = agentRepo.get(db, resolvedId)
|
|
164
|
-
if (!agent) return c.json({ error: 'agent vanished after update' }, 404)
|
|
165
|
-
return c.json(agent)
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
agentsRouter.delete('/:id', (c) => {
|
|
169
|
-
const { db, paths, authToken } = getCtx()
|
|
170
|
-
try {
|
|
171
|
-
deleteAgent(db, c.req.param('id'))
|
|
172
|
-
return c.body(null, 204)
|
|
173
|
-
} catch (err) {
|
|
174
|
-
return c.json({ error: (err as Error).message }, 400)
|
|
175
|
-
}
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
agentsRouter.post('/:id/archive', (c) => {
|
|
179
|
-
const { db, paths, authToken } = getCtx()
|
|
180
|
-
try {
|
|
181
|
-
archiveAgent(db, c.req.param('id'))
|
|
182
|
-
return c.body(null, 204)
|
|
183
|
-
} catch (err) {
|
|
184
|
-
return c.json({ error: (err as Error).message }, 400)
|
|
185
|
-
}
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
agentsRouter.post('/:id/unarchive', (c) => {
|
|
189
|
-
const { db, paths, authToken } = getCtx()
|
|
190
|
-
try {
|
|
191
|
-
unarchiveAgent(db, c.req.param('id'))
|
|
192
|
-
return c.body(null, 204)
|
|
193
|
-
} catch (err) {
|
|
194
|
-
return c.json({ error: (err as Error).message }, 400)
|
|
195
|
-
}
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
// ─── Group move ──────────────────────────────────────────────────────────
|
|
199
|
-
|
|
200
|
-
agentsRouter.patch('/:id/group', async (c) => {
|
|
201
|
-
const body = (await c.req.json().catch(() => null)) as MoveAgentRequest | null
|
|
202
|
-
if (!body || typeof body.groupId !== 'string' || !body.groupId) {
|
|
203
|
-
return c.json({ error: 'groupId (string) is required' }, 400)
|
|
204
|
-
}
|
|
205
|
-
const { db, paths, authToken } = getCtx()
|
|
206
|
-
let resolved: ReturnType<typeof resolveAgent>
|
|
207
|
-
try {
|
|
208
|
-
resolved = resolveAgent(db, paths, c.req.param('id'))
|
|
209
|
-
} catch {
|
|
210
|
-
return c.json({ error: `agent not found: ${c.req.param('id')}` }, 404)
|
|
211
|
-
}
|
|
212
|
-
if (!groupRepo.get(db, body.groupId, paths)) {
|
|
213
|
-
return c.json({ error: `group not found: ${body.groupId}` }, 404)
|
|
214
|
-
}
|
|
215
|
-
agentRepo.setGroup(db, resolved.agent.id, body.groupId)
|
|
216
|
-
return c.json(resolveAgent(db, paths, resolved.agent.id))
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
// ─── Skills ──────────────────────────────────────────────────────────────
|
|
220
|
-
|
|
221
|
-
agentsRouter.get('/:id/skills', (c) => {
|
|
222
|
-
const { db, paths, authToken } = getCtx()
|
|
223
|
-
const id = resolveAgentIdParam(db, c.req.param('id'))
|
|
224
|
-
const set = resolveAgentSkills(db, paths, id)
|
|
225
|
-
const body: ResolvedSkillsResponse = {
|
|
226
|
-
resolved: set.resolved.map((s) => {
|
|
227
|
-
const meta = skillMetaRepo.get(db, s.name)
|
|
228
|
-
return {
|
|
229
|
-
name: s.name,
|
|
230
|
-
description: s.parsed.frontmatter.description,
|
|
231
|
-
source: meta?.source ?? null,
|
|
232
|
-
importedAt: meta?.importedAt ?? null,
|
|
233
|
-
}
|
|
234
|
-
}),
|
|
235
|
-
missing: set.missing,
|
|
236
|
-
}
|
|
237
|
-
return c.json(body)
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
agentsRouter.post('/:id/skills', async (c) => {
|
|
241
|
-
const body = (await c.req.json().catch(() => null)) as AttachSkillRequest | null
|
|
242
|
-
if (!body || typeof body.skill !== 'string' || !body.skill) {
|
|
243
|
-
return c.json({ error: 'skill is required' }, 400)
|
|
244
|
-
}
|
|
245
|
-
const { db, paths, authToken } = getCtx()
|
|
246
|
-
const agent = agentRepo.get(db, c.req.param('id'))
|
|
247
|
-
if (!agent) return c.json({ error: `agent not found: ${c.req.param('id')}` }, 404)
|
|
248
|
-
agentRepo.attachSkill(db, agent.id, body.skill)
|
|
249
|
-
return c.body(null, 204)
|
|
250
|
-
})
|
|
251
|
-
|
|
252
|
-
agentsRouter.delete('/:id/skills/:name', (c) => {
|
|
253
|
-
const { db, paths, authToken } = getCtx()
|
|
254
|
-
const id = resolveAgentIdParam(db, c.req.param('id'))
|
|
255
|
-
agentRepo.detachSkill(db, id, c.req.param('name'))
|
|
256
|
-
return c.body(null, 204)
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
// ─── Triggers ────────────────────────────────────────────────────────────
|
|
260
|
-
|
|
261
|
-
agentsRouter.get('/:id/triggers', (c) => {
|
|
262
|
-
const { db, paths, authToken } = getCtx()
|
|
263
|
-
const agent = agentRepo.get(db, c.req.param('id'))
|
|
264
|
-
if (!agent) return c.json({ error: `agent not found: ${c.req.param('id')}` }, 404)
|
|
265
|
-
return c.json({ triggers: triggerRepo.listForAgent(db, agent.id) })
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
agentsRouter.post('/:id/triggers', async (c) => {
|
|
269
|
-
const body = (await c.req.json().catch(() => null)) as CreateTriggerRequest | null
|
|
270
|
-
if (!body) return c.json({ error: 'invalid JSON body' }, 400)
|
|
271
|
-
if (typeof body.message !== 'string' || !body.message.trim()) {
|
|
272
|
-
return c.json({ error: 'message is required' }, 400)
|
|
273
|
-
}
|
|
274
|
-
const { db, paths, authToken } = getCtx()
|
|
275
|
-
const agent = agentRepo.get(db, c.req.param('id'))
|
|
276
|
-
if (!agent) return c.json({ error: `agent not found: ${c.req.param('id')}` }, 404)
|
|
277
|
-
|
|
278
|
-
if (body.kind === 'interval') {
|
|
279
|
-
if (!Number.isFinite(body.intervalSec) || (body.intervalSec ?? 0) <= 0) {
|
|
280
|
-
return c.json({ error: 'intervalSec must be a positive number' }, 400)
|
|
281
|
-
}
|
|
282
|
-
const trigger = triggerRepo.insert(db, {
|
|
283
|
-
agentId: agent.id,
|
|
284
|
-
kind: 'interval',
|
|
285
|
-
intervalSec: Math.floor(body.intervalSec as number),
|
|
286
|
-
cronExpr: null,
|
|
287
|
-
message: body.message,
|
|
288
|
-
enabled: body.enabled,
|
|
289
|
-
})
|
|
290
|
-
return c.json({ trigger }, 201)
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (body.kind === 'cron') {
|
|
294
|
-
if (typeof body.cronExpr !== 'string' || !body.cronExpr.trim()) {
|
|
295
|
-
return c.json({ error: 'cronExpr is required for kind=cron' }, 400)
|
|
296
|
-
}
|
|
297
|
-
try {
|
|
298
|
-
validateCron(body.cronExpr)
|
|
299
|
-
} catch (err) {
|
|
300
|
-
return c.json({ error: `invalid cron: ${(err as Error).message}` }, 400)
|
|
301
|
-
}
|
|
302
|
-
const trigger = triggerRepo.insert(db, {
|
|
303
|
-
agentId: agent.id,
|
|
304
|
-
kind: 'cron',
|
|
305
|
-
intervalSec: null,
|
|
306
|
-
cronExpr: body.cronExpr.trim(),
|
|
307
|
-
message: body.message,
|
|
308
|
-
enabled: body.enabled,
|
|
309
|
-
})
|
|
310
|
-
return c.json({ trigger }, 201)
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return c.json({ error: `invalid kind: ${body.kind}` }, 400)
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
// ─── Messages ────────────────────────────────────────────────────────────
|
|
317
|
-
|
|
318
|
-
agentsRouter.get('/:id/messages', (c) => {
|
|
319
|
-
const { db, paths, authToken } = getCtx()
|
|
320
|
-
const agent = agentRepo.get(db, c.req.param('id'))
|
|
321
|
-
if (!agent) return c.json({ error: `agent not found: ${c.req.param('id')}` }, 404)
|
|
322
|
-
const unreadOnly = c.req.query('unread') === '1'
|
|
323
|
-
const body: ListInboxResponse = {
|
|
324
|
-
messages: messageRepo.listInbox(db, agent.id, { unreadOnly }),
|
|
325
|
-
}
|
|
326
|
-
return c.json(body)
|
|
327
|
-
})
|
|
328
|
-
|
|
329
|
-
agentsRouter.post('/:id/messages', async (c) => {
|
|
330
|
-
const body = (await c.req.json().catch(() => null)) as SendMessageRequest | null
|
|
331
|
-
if (
|
|
332
|
-
!body ||
|
|
333
|
-
typeof body.from !== 'string' ||
|
|
334
|
-
!body.from ||
|
|
335
|
-
!body.payload ||
|
|
336
|
-
typeof body.payload.text !== 'string'
|
|
337
|
-
) {
|
|
338
|
-
return c.json({ error: 'from and payload.text are required' }, 400)
|
|
339
|
-
}
|
|
340
|
-
const { db, paths, authToken } = getCtx()
|
|
341
|
-
const fromAgent = agentRepo.get(db, body.from)
|
|
342
|
-
if (!fromAgent) return c.json({ error: `agent not found: ${body.from}` }, 404)
|
|
343
|
-
const toAgent = agentRepo.get(db, c.req.param('id'))
|
|
344
|
-
if (!toAgent) return c.json({ error: `agent not found: ${c.req.param('id')}` }, 404)
|
|
345
|
-
if (body.replyTo && !messageRepo.get(db, body.replyTo)) {
|
|
346
|
-
return c.json({ error: `reply target not found: ${body.replyTo}` }, 404)
|
|
347
|
-
}
|
|
348
|
-
const msg = messageRepo.send(db, {
|
|
349
|
-
from: fromAgent.id,
|
|
350
|
-
to: toAgent.id,
|
|
351
|
-
payload: JSON.stringify({ text: body.payload.text }),
|
|
352
|
-
replyTo: body.replyTo ?? null,
|
|
353
|
-
})
|
|
354
|
-
return c.json(msg, 201)
|
|
355
|
-
})
|
|
356
|
-
|
|
357
|
-
// ─── Cancel ──────────────────────────────────────────────────────────────
|
|
358
|
-
|
|
359
|
-
// Aborts the agent's currently-running turn, if any. Returns 204 on a
|
|
360
|
-
// successful abort, 409 when the agent is idle. Cancellation drives off the
|
|
361
|
-
// in-memory agent-cancel registry, which is also what the scheduler probes
|
|
362
|
-
// to skip overlapping inbox wakes / triggers.
|
|
363
|
-
agentsRouter.post('/:id/cancel', (c) => {
|
|
364
|
-
const { db, paths, authToken } = getCtx()
|
|
365
|
-
const id = resolveAgentIdParam(db, c.req.param('id'))
|
|
366
|
-
const cancelled = cancelAgent(id)
|
|
367
|
-
if (!cancelled) return c.json({ error: 'agent has no active turn' }, 409)
|
|
368
|
-
return c.body(null, 204)
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
// ─── Sessions ────────────────────────────────────────────────────────────
|
|
372
|
-
|
|
373
|
-
agentsRouter.get('/:id/sessions/head', (c) => {
|
|
374
|
-
const { db, paths, authToken } = getCtx()
|
|
375
|
-
let resolved: ReturnType<typeof resolveAgent>
|
|
376
|
-
try {
|
|
377
|
-
resolved = resolveAgent(db, paths, c.req.param('id'))
|
|
378
|
-
} catch {
|
|
379
|
-
return c.json({ error: `agent not found: ${c.req.param('id')}` }, 404)
|
|
380
|
-
}
|
|
381
|
-
const head: SessionHeadResponse = loadSessionHead(resolved, paths)
|
|
382
|
-
return c.json(head)
|
|
383
|
-
})
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Returns the agent's prior transcript flattened to ProviderMessage[].
|
|
387
|
-
* SSR loaders use it to render the chat history on first paint without
|
|
388
|
-
* touching pi or the filesystem from the web process.
|
|
389
|
-
*/
|
|
390
|
-
agentsRouter.get('/:id/sessions/messages', (c) => {
|
|
391
|
-
const { db, paths, authToken } = getCtx()
|
|
392
|
-
let resolved: ReturnType<typeof resolveAgent>
|
|
393
|
-
try {
|
|
394
|
-
resolved = resolveAgent(db, paths, c.req.param('id'))
|
|
395
|
-
} catch {
|
|
396
|
-
return c.json({ error: `agent not found: ${c.req.param('id')}` }, 404)
|
|
397
|
-
}
|
|
398
|
-
const messages = piMessagesToProviderView(loadInitialMessages(resolved, paths))
|
|
399
|
-
return c.json({ messages })
|
|
400
|
-
})
|
|
401
|
-
|
|
402
|
-
// ─── Chat ────────────────────────────────────────────────────────────────
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Streaming chat endpoint. Server-authoritative: prior history is read by
|
|
406
|
-
* pi's SessionManager from the agent's JSONL session file. The client sends
|
|
407
|
-
* only `{ message }`. Response is NDJSON-encoded `ChatFrame`s.
|
|
408
|
-
*/
|
|
409
|
-
agentsRouter.post('/:id/chat', async (c) => {
|
|
410
|
-
const { db, paths, authToken } = getCtx()
|
|
411
|
-
const id = resolveAgentIdParam(db, c.req.param('id'))
|
|
412
|
-
|
|
413
|
-
let body: { message?: string }
|
|
414
|
-
try {
|
|
415
|
-
body = (await c.req.json()) as { message?: string }
|
|
416
|
-
} catch {
|
|
417
|
-
return c.json({ error: 'invalid JSON body' }, 400)
|
|
418
|
-
}
|
|
419
|
-
const message = body.message
|
|
420
|
-
if (!message || typeof message !== 'string') {
|
|
421
|
-
return c.json({ error: 'message is required' }, 400)
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
const stream = new ReadableStream({
|
|
425
|
-
async start(controller) {
|
|
426
|
-
const encoder = new TextEncoder()
|
|
427
|
-
try {
|
|
428
|
-
for await (const frame of runAgentTurn(id, message)) {
|
|
429
|
-
try {
|
|
430
|
-
controller.enqueue(encoder.encode(`${JSON.stringify(frame)}\n`))
|
|
431
|
-
} catch {
|
|
432
|
-
// client disconnected — keep draining so state gets saved
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
} catch (err) {
|
|
436
|
-
try {
|
|
437
|
-
controller.enqueue(
|
|
438
|
-
encoder.encode(`${JSON.stringify({ kind: 'fatal', error: (err as Error).message })}\n`),
|
|
439
|
-
)
|
|
440
|
-
} catch {}
|
|
441
|
-
}
|
|
442
|
-
try {
|
|
443
|
-
controller.close()
|
|
444
|
-
} catch {}
|
|
445
|
-
},
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
return new Response(stream, {
|
|
449
|
-
headers: {
|
|
450
|
-
'content-type': 'application/x-ndjson',
|
|
451
|
-
'cache-control': 'no-cache',
|
|
452
|
-
'x-content-type-options': 'nosniff',
|
|
453
|
-
},
|
|
454
|
-
})
|
|
455
|
-
})
|
|
456
|
-
|
|
457
|
-
agentsRouter.post('/:id/chat/compact', async (c) => {
|
|
458
|
-
let body: ChatCompactRequest = {}
|
|
459
|
-
if (c.req.header('content-length') !== '0') {
|
|
460
|
-
try {
|
|
461
|
-
const parsed = (await c.req.json()) as ChatCompactRequest | null
|
|
462
|
-
if (parsed && typeof parsed === 'object') body = parsed
|
|
463
|
-
} catch {
|
|
464
|
-
// empty body is allowed — all fields optional
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
const { db, paths, authToken } = getCtx()
|
|
469
|
-
const id = resolveAgentIdParam(db, c.req.param('id'))
|
|
470
|
-
if (!agentRepo.get(db, id)) return c.json({ error: 'agent not found' }, 404)
|
|
471
|
-
|
|
472
|
-
const resolved = resolveAgent(db, paths, id)
|
|
473
|
-
const memory = qmdBackend(join(resolved.group.path, 'memory'))
|
|
474
|
-
await memory.init()
|
|
475
|
-
const env = mergeSecretsIntoEnv(db, authToken)
|
|
476
|
-
|
|
477
|
-
const apiKeyResolution = await resolveAgentApiKey(db, authToken, resolved, {
|
|
478
|
-
withRefresher: true,
|
|
479
|
-
})
|
|
480
|
-
const handle = await createBazilionSession({
|
|
481
|
-
agent: resolved,
|
|
482
|
-
paths,
|
|
483
|
-
env,
|
|
484
|
-
memory,
|
|
485
|
-
enabledProviders: providerStateRepo.listEnabled(db),
|
|
486
|
-
messagingHost: createDbMessagingHost(db),
|
|
487
|
-
...apiKeyResolution,
|
|
488
|
-
})
|
|
489
|
-
try {
|
|
490
|
-
const entriesBefore = handle.session.sessionManager.getEntries().length
|
|
491
|
-
const result = await handle.session.compact(body.customInstructions)
|
|
492
|
-
const entriesAfter = handle.session.sessionManager.getEntries().length
|
|
493
|
-
|
|
494
|
-
let keptTail = 0
|
|
495
|
-
if (result.firstKeptEntryId) {
|
|
496
|
-
const branch = handle.session.sessionManager.getBranch()
|
|
497
|
-
const idx = branch.findIndex((e) => e.id === result.firstKeptEntryId)
|
|
498
|
-
if (idx >= 0) for (const e of branch.slice(idx)) if (e.type === 'message') keptTail++
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
const tokensAfter = handle.session.getContextUsage()?.tokens ?? 0
|
|
502
|
-
|
|
503
|
-
const resp: ChatCompactResponse = {
|
|
504
|
-
before: entriesBefore,
|
|
505
|
-
after: entriesAfter,
|
|
506
|
-
summarized: Math.max(0, entriesBefore - entriesAfter + 1),
|
|
507
|
-
keptTail,
|
|
508
|
-
tokensBefore: result.tokensBefore,
|
|
509
|
-
tokensAfter,
|
|
510
|
-
summary: result.summary,
|
|
511
|
-
}
|
|
512
|
-
return c.json(resp)
|
|
513
|
-
} catch (err) {
|
|
514
|
-
return c.json({ error: (err as Error).message }, 502)
|
|
515
|
-
} finally {
|
|
516
|
-
handle.dispose()
|
|
517
|
-
}
|
|
518
|
-
})
|
|
519
|
-
|
|
520
|
-
agentsRouter.get('/:id/chat/context', async (c) => {
|
|
521
|
-
const { db, paths, authToken } = getCtx()
|
|
522
|
-
const id = resolveAgentIdParam(db, c.req.param('id'))
|
|
523
|
-
if (!agentRepo.get(db, id)) return c.json({ error: 'agent not found' }, 404)
|
|
524
|
-
|
|
525
|
-
const resolved = resolveAgent(db, paths, id)
|
|
526
|
-
|
|
527
|
-
const files: ContextFileEntry[] = []
|
|
528
|
-
for (const file of CONTEXT_FILE_ORDER) {
|
|
529
|
-
const path = join(resolved.agent.dir, file)
|
|
530
|
-
if (!existsSync(path)) continue
|
|
531
|
-
const content = readFileSync(path, 'utf8').trimEnd()
|
|
532
|
-
if (!content) continue
|
|
533
|
-
const chars = content.length + file.length + 6
|
|
534
|
-
files.push({ name: file, chars, tokens: estimateTokens(chars) })
|
|
535
|
-
}
|
|
536
|
-
const systemPromptText = buildSystemPrompt(resolved)
|
|
537
|
-
const systemPromptChars = systemPromptText.length
|
|
538
|
-
|
|
539
|
-
const skillsListChars =
|
|
540
|
-
resolved.skills.length > 0
|
|
541
|
-
? `# Available Skills\n\nYou have access to the following skills: ${resolved.skills.join(', ')}.`
|
|
542
|
-
.length
|
|
543
|
-
: 0
|
|
544
|
-
const groupLines = [
|
|
545
|
-
'# Group',
|
|
546
|
-
'',
|
|
547
|
-
`- ${resolved.group.id} (${resolved.group.name}): ${resolved.group.path}`,
|
|
548
|
-
'',
|
|
549
|
-
'Your group is where work product lives — code, docs, artefacts, shared scratch. It may be shared with other agents in the same group. Your coding tools (`read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`) are rooted at the group directory. Never use these tools to edit your identity/soul/behaviour files — those live in your home and are reached via `home_write` / `home_read`.',
|
|
550
|
-
]
|
|
551
|
-
const groupListChars = groupLines.join('\n').length
|
|
552
|
-
const userMdChars = resolved.group.userMd.trim()
|
|
553
|
-
? `# About the User\n\nRead-only context about the human you're working with in this group. You cannot edit this — if it's wrong, say so and they will update it.\n\n${resolved.group.userMd.trim()}`
|
|
554
|
-
.length
|
|
555
|
-
: 0
|
|
556
|
-
const memoryHintChars =
|
|
557
|
-
'# Memory\n\nYou have a persistent memory backend. Use `memory_write` to remember things across sessions, and `memory_search` / `memory_read` / `memory_list` to recall them. Always check memory at the start of a session if the user might have told you something important before.'
|
|
558
|
-
.length
|
|
559
|
-
|
|
560
|
-
const memory = qmdBackend(join(resolved.group.path, 'memory'))
|
|
561
|
-
await memory.init()
|
|
562
|
-
const env = mergeSecretsIntoEnv(db, authToken)
|
|
563
|
-
const apiKeyResolution = await resolveAgentApiKey(db, authToken, resolved, {
|
|
564
|
-
withRefresher: true,
|
|
565
|
-
})
|
|
566
|
-
const handle = await createBazilionSession({
|
|
567
|
-
agent: resolved,
|
|
568
|
-
paths,
|
|
569
|
-
env,
|
|
570
|
-
memory,
|
|
571
|
-
enabledProviders: providerStateRepo.listEnabled(db),
|
|
572
|
-
messagingHost: createDbMessagingHost(db),
|
|
573
|
-
...apiKeyResolution,
|
|
574
|
-
})
|
|
575
|
-
|
|
576
|
-
try {
|
|
577
|
-
const toolInfos = handle.session.getAllTools()
|
|
578
|
-
let toolsSchemaChars = 0
|
|
579
|
-
let toolsListChars = 0
|
|
580
|
-
const toolEntries: ContextToolEntry[] = []
|
|
581
|
-
for (const info of toolInfos) {
|
|
582
|
-
const schemaJson = JSON.stringify(info.parameters ?? {})
|
|
583
|
-
const schemaChars = schemaJson.length
|
|
584
|
-
const descriptionChars = info.description.length
|
|
585
|
-
toolsSchemaChars += schemaChars
|
|
586
|
-
toolsListChars += info.name.length + descriptionChars + 3
|
|
587
|
-
toolEntries.push({
|
|
588
|
-
name: info.name,
|
|
589
|
-
schemaChars,
|
|
590
|
-
descriptionChars,
|
|
591
|
-
paramCount: countProperties(info.parameters),
|
|
592
|
-
})
|
|
593
|
-
}
|
|
594
|
-
toolEntries.sort((a, b) => b.schemaChars - a.schemaChars)
|
|
595
|
-
|
|
596
|
-
const installed = discoverSkills(paths)
|
|
597
|
-
const skillEntries: ContextSkillEntry[] = []
|
|
598
|
-
for (const name of resolved.skills) {
|
|
599
|
-
const match = installed.find((s) => s.name === name)
|
|
600
|
-
let blockChars = name.length + 2
|
|
601
|
-
if (match) {
|
|
602
|
-
try {
|
|
603
|
-
blockChars = readFileSync(match.skillFile, 'utf8').length
|
|
604
|
-
} catch {}
|
|
605
|
-
}
|
|
606
|
-
skillEntries.push({ name, blockChars })
|
|
607
|
-
}
|
|
608
|
-
skillEntries.sort((a, b) => b.blockChars - a.blockChars)
|
|
609
|
-
|
|
610
|
-
const group: ContextGroupEntry = {
|
|
611
|
-
id: resolved.group.id,
|
|
612
|
-
name: resolved.group.name,
|
|
613
|
-
path: resolved.group.path,
|
|
614
|
-
userMdChars: resolved.group.userMd.length,
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
const stats = handle.session.getSessionStats()
|
|
618
|
-
const historyChars = stats.tokens.total * 4
|
|
619
|
-
const messageEntries = stats.userMessages + stats.assistantMessages + stats.toolResults
|
|
620
|
-
const compactionEntries = handle.session.sessionManager
|
|
621
|
-
.getEntries()
|
|
622
|
-
.filter((e) => e.type === 'compaction').length
|
|
623
|
-
const contextUsage = handle.session.getContextUsage()
|
|
624
|
-
const historyTokens = contextUsage?.tokens ?? stats.tokens.total
|
|
625
|
-
|
|
626
|
-
const detail = c.req.query('detail') === '1' || c.req.query('json') === '1'
|
|
627
|
-
const CAP = 30
|
|
628
|
-
const toolEntriesOut = detail ? toolEntries : toolEntries.slice(0, CAP)
|
|
629
|
-
const skillEntriesOut = detail ? skillEntries : skillEntries.slice(0, CAP)
|
|
630
|
-
|
|
631
|
-
const totalsChars = systemPromptChars + toolsSchemaChars + historyChars
|
|
632
|
-
const resp: ChatContextResponse = {
|
|
633
|
-
agentId: resolved.agent.id,
|
|
634
|
-
model: resolved.model,
|
|
635
|
-
systemPrompt: {
|
|
636
|
-
chars: systemPromptChars,
|
|
637
|
-
tokens: estimateTokens(systemPromptChars),
|
|
638
|
-
files,
|
|
639
|
-
skillsListChars,
|
|
640
|
-
groupListChars,
|
|
641
|
-
userMdChars,
|
|
642
|
-
memoryHintChars,
|
|
643
|
-
},
|
|
644
|
-
tools: {
|
|
645
|
-
count: toolInfos.length,
|
|
646
|
-
listChars: toolsListChars,
|
|
647
|
-
schemaChars: toolsSchemaChars,
|
|
648
|
-
entries: toolEntriesOut,
|
|
649
|
-
},
|
|
650
|
-
skills: {
|
|
651
|
-
count: resolved.skills.length,
|
|
652
|
-
entries: skillEntriesOut,
|
|
653
|
-
},
|
|
654
|
-
group,
|
|
655
|
-
history: {
|
|
656
|
-
messageEntries,
|
|
657
|
-
compactionEntries,
|
|
658
|
-
chars: historyChars,
|
|
659
|
-
bytes: historyChars,
|
|
660
|
-
tokensEstimate: historyTokens,
|
|
661
|
-
},
|
|
662
|
-
totals: {
|
|
663
|
-
chars: totalsChars,
|
|
664
|
-
tokens: estimateTokens(totalsChars),
|
|
665
|
-
},
|
|
666
|
-
}
|
|
667
|
-
return c.json(resp)
|
|
668
|
-
} finally {
|
|
669
|
-
handle.dispose()
|
|
670
|
-
}
|
|
671
|
-
})
|
|
672
|
-
|
|
673
|
-
agentsRouter.post('/:id/chat/reset', (c) => {
|
|
674
|
-
const { db, paths, authToken } = getCtx()
|
|
675
|
-
const id = resolveAgentIdParam(db, c.req.param('id'))
|
|
676
|
-
const agent = agentRepo.get(db, id)
|
|
677
|
-
if (!agent) return c.json({ error: 'agent not found' }, 404)
|
|
678
|
-
|
|
679
|
-
const sessionsDir = join(paths.agentDir(agent.id), 'sessions')
|
|
680
|
-
let deleted = 0
|
|
681
|
-
if (existsSync(sessionsDir)) {
|
|
682
|
-
for (const file of readdirSync(sessionsDir)) {
|
|
683
|
-
if (!file.endsWith('.jsonl')) continue
|
|
684
|
-
try {
|
|
685
|
-
rmSync(join(sessionsDir, file))
|
|
686
|
-
deleted++
|
|
687
|
-
} catch {
|
|
688
|
-
// best-effort
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
return c.json({ ok: true, deletedSessionFiles: deleted })
|
|
693
|
-
})
|
|
694
|
-
|
|
695
|
-
agentsRouter.post('/:id/chat/truncate', async (c) => {
|
|
696
|
-
let body: TruncateChatRequest
|
|
697
|
-
try {
|
|
698
|
-
body = (await c.req.json()) as TruncateChatRequest
|
|
699
|
-
} catch {
|
|
700
|
-
return c.json({ error: 'invalid JSON body' }, 400)
|
|
701
|
-
}
|
|
702
|
-
const keep = Number(body.keepCount)
|
|
703
|
-
if (!Number.isFinite(keep) || keep < 0 || !Number.isInteger(keep)) {
|
|
704
|
-
return c.json({ error: 'keepCount must be a non-negative integer' }, 400)
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const { db, paths, authToken } = getCtx()
|
|
708
|
-
const id = resolveAgentIdParam(db, c.req.param('id'))
|
|
709
|
-
if (!agentRepo.get(db, id)) return c.json({ error: 'agent not found' }, 404)
|
|
710
|
-
|
|
711
|
-
const resolved = resolveAgent(db, paths, id)
|
|
712
|
-
const memory = qmdBackend(join(resolved.group.path, 'memory'))
|
|
713
|
-
await memory.init()
|
|
714
|
-
const env = mergeSecretsIntoEnv(db, authToken)
|
|
715
|
-
|
|
716
|
-
const apiKeyResolution = await resolveAgentApiKey(db, authToken, resolved, {
|
|
717
|
-
withRefresher: true,
|
|
718
|
-
})
|
|
719
|
-
const handle = await createBazilionSession({
|
|
720
|
-
agent: resolved,
|
|
721
|
-
paths,
|
|
722
|
-
env,
|
|
723
|
-
memory,
|
|
724
|
-
enabledProviders: providerStateRepo.listEnabled(db),
|
|
725
|
-
messagingHost: createDbMessagingHost(db),
|
|
726
|
-
...apiKeyResolution,
|
|
727
|
-
})
|
|
728
|
-
try {
|
|
729
|
-
const branch = handle.session.sessionManager.getBranch()
|
|
730
|
-
const messageEntries = branch.filter((e) => e.type === 'message')
|
|
731
|
-
const before = messageEntries.length
|
|
732
|
-
const target = Math.max(0, Math.min(keep, before))
|
|
733
|
-
|
|
734
|
-
if (target === before) {
|
|
735
|
-
return c.json({ before, after: before } satisfies TruncateChatResponse)
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
if (target === 0) {
|
|
739
|
-
handle.session.sessionManager.resetLeaf()
|
|
740
|
-
} else {
|
|
741
|
-
const lastKept = messageEntries[target - 1]
|
|
742
|
-
if (!lastKept) return c.json({ error: 'internal: missing target entry' }, 500)
|
|
743
|
-
handle.session.sessionManager.branch(lastKept.id)
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
return c.json({ before, after: target } satisfies TruncateChatResponse)
|
|
747
|
-
} finally {
|
|
748
|
-
handle.dispose()
|
|
749
|
-
}
|
|
750
|
-
})
|
|
751
|
-
|
|
752
|
-
// ─── helpers ─────────────────────────────────────────────────────────────
|
|
753
|
-
|
|
754
|
-
const CONTEXT_FILE_ORDER = [
|
|
755
|
-
'AGENTS.md',
|
|
756
|
-
'SOUL.md',
|
|
757
|
-
'TOOLS.md',
|
|
758
|
-
'IDENTITY.md',
|
|
759
|
-
'HEARTBEAT.md',
|
|
760
|
-
'BOOTSTRAP.md',
|
|
761
|
-
] as const
|
|
762
|
-
|
|
763
|
-
function estimateTokens(chars: number): number {
|
|
764
|
-
return Math.ceil(Math.max(0, chars) / 4)
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
function countProperties(schema: unknown): number | null {
|
|
768
|
-
if (!schema || typeof schema !== 'object') return null
|
|
769
|
-
const props = (schema as { properties?: unknown }).properties
|
|
770
|
-
if (!props || typeof props !== 'object') return null
|
|
771
|
-
return Object.keys(props as Record<string, unknown>).length
|
|
772
|
-
}
|