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,121 +0,0 @@
|
|
|
1
|
-
// OpenAI ChatGPT / Codex OAuth — token storage + refresh on top of pi-ai.
|
|
2
|
-
//
|
|
3
|
-
// Pi-ai ships a complete OAuth flow for the ChatGPT backend (`@mariozechner/pi-ai`
|
|
4
|
-
// exports `loginOpenAICodex` + `refreshOpenAICodexToken`), so this module is
|
|
5
|
-
// thin: it adapts the credential I/O to Bazilion's encrypted secrets store
|
|
6
|
-
// and exposes a single `loadAccessToken(db, authToken)` call that refreshes
|
|
7
|
-
// when the token is about to expire.
|
|
8
|
-
//
|
|
9
|
-
// Storage: the JSON blob `{refresh, access, expires}` lives under the
|
|
10
|
-
// secrets key `OPENAI_CODEX_OAUTH` in the `secrets` table. The blob is
|
|
11
|
-
// never copied into the env (unlike plain-API-key providers) — refresh is
|
|
12
|
-
// stateful, so every call reads and writes through the live secrets store.
|
|
13
|
-
|
|
14
|
-
import type { OpenAICodexStatus } from '@bazilion/api-types'
|
|
15
|
-
import type { OAuthCredentials } from '@mariozechner/pi-ai'
|
|
16
|
-
import { loginOpenAICodex, refreshOpenAICodexToken } from '@mariozechner/pi-ai/oauth'
|
|
17
|
-
import { type BazilionDb, openSecrets } from '../../core/index.ts'
|
|
18
|
-
|
|
19
|
-
export const OPENAI_CODEX_SECRET_KEY = 'OPENAI_CODEX_OAUTH'
|
|
20
|
-
|
|
21
|
-
/** Refresh when the access token has less than this much life left. */
|
|
22
|
-
const REFRESH_MARGIN_MS = 60_000
|
|
23
|
-
|
|
24
|
-
export interface StoredCredentials {
|
|
25
|
-
refresh: string
|
|
26
|
-
access: string
|
|
27
|
-
expires: number
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function readCredentials(db: BazilionDb, authToken: string): StoredCredentials | null {
|
|
31
|
-
const raw = openSecrets(db, authToken).get(OPENAI_CODEX_SECRET_KEY)
|
|
32
|
-
if (!raw) return null
|
|
33
|
-
try {
|
|
34
|
-
const parsed = JSON.parse(raw) as Partial<StoredCredentials>
|
|
35
|
-
if (
|
|
36
|
-
typeof parsed.refresh === 'string' &&
|
|
37
|
-
typeof parsed.access === 'string' &&
|
|
38
|
-
typeof parsed.expires === 'number'
|
|
39
|
-
) {
|
|
40
|
-
return { refresh: parsed.refresh, access: parsed.access, expires: parsed.expires }
|
|
41
|
-
}
|
|
42
|
-
return null
|
|
43
|
-
} catch {
|
|
44
|
-
return null
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function writeCredentials(db: BazilionDb, authToken: string, creds: StoredCredentials): void {
|
|
49
|
-
openSecrets(db, authToken).set(OPENAI_CODEX_SECRET_KEY, JSON.stringify(creds))
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function clearCredentials(db: BazilionDb, authToken: string): void {
|
|
53
|
-
openSecrets(db, authToken).remove(OPENAI_CODEX_SECRET_KEY)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function hasCredentials(db: BazilionDb, authToken: string): boolean {
|
|
57
|
-
return readCredentials(db, authToken) !== null
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function decodeAccountId(accessToken: string): string | null {
|
|
61
|
-
const parts = accessToken.split('.')
|
|
62
|
-
if (parts.length !== 3) return null
|
|
63
|
-
try {
|
|
64
|
-
const payload = JSON.parse(Buffer.from(parts[1] as string, 'base64').toString('utf8')) as {
|
|
65
|
-
'https://api.openai.com/auth'?: { chatgpt_account_id?: string }
|
|
66
|
-
}
|
|
67
|
-
return payload['https://api.openai.com/auth']?.chatgpt_account_id ?? null
|
|
68
|
-
} catch {
|
|
69
|
-
return null
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function getStatus(db: BazilionDb, authToken: string): OpenAICodexStatus {
|
|
74
|
-
const creds = readCredentials(db, authToken)
|
|
75
|
-
if (!creds) return { connected: false, expiresAt: null, accountId: null }
|
|
76
|
-
return {
|
|
77
|
-
connected: true,
|
|
78
|
-
expiresAt: creds.expires,
|
|
79
|
-
accountId: decodeAccountId(creds.access),
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Returns a valid access token, refreshing via pi-ai if the stored one is
|
|
85
|
-
* within `REFRESH_MARGIN_MS` of expiry. Throws when no credentials are stored
|
|
86
|
-
* so callers can surface an actionable "run `bazilion auth openai login`"
|
|
87
|
-
* error rather than a 401 from the upstream API.
|
|
88
|
-
*/
|
|
89
|
-
export async function loadAccessToken(db: BazilionDb, authToken: string): Promise<string> {
|
|
90
|
-
const creds = readCredentials(db, authToken)
|
|
91
|
-
if (!creds) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
'OpenAI ChatGPT OAuth not configured — run `bazilion auth openai login` (or use the Connect button on /config)',
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
if (creds.expires > Date.now() + REFRESH_MARGIN_MS) return creds.access
|
|
97
|
-
|
|
98
|
-
const refreshed = (await refreshOpenAICodexToken(creds.refresh)) as OAuthCredentials
|
|
99
|
-
const next: StoredCredentials = {
|
|
100
|
-
refresh: refreshed.refresh,
|
|
101
|
-
access: refreshed.access,
|
|
102
|
-
expires: refreshed.expires,
|
|
103
|
-
}
|
|
104
|
-
writeCredentials(db, authToken, next)
|
|
105
|
-
return next.access
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/** Persist credentials fetched by pi-ai's `loginOpenAICodex`. */
|
|
109
|
-
export function saveLoginCredentials(
|
|
110
|
-
db: BazilionDb,
|
|
111
|
-
authToken: string,
|
|
112
|
-
creds: OAuthCredentials,
|
|
113
|
-
): void {
|
|
114
|
-
writeCredentials(db, authToken, {
|
|
115
|
-
refresh: creds.refresh,
|
|
116
|
-
access: creds.access,
|
|
117
|
-
expires: creds.expires,
|
|
118
|
-
})
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export { loginOpenAICodex, refreshOpenAICodexToken }
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// Default heartbeat prompt. Users paste this constant as a trigger's
|
|
2
|
-
// `message` (or call `resolveHeartbeatPrompt` with a custom one) to wire
|
|
3
|
-
// HEARTBEAT.md into a scheduled wake-up without reinventing the framing.
|
|
4
|
-
export const HEARTBEAT_PROMPT =
|
|
5
|
-
'Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.'
|
|
6
|
-
|
|
7
|
-
export const DEFAULT_HEARTBEAT_EVERY_SEC = 30 * 60
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* A HEARTBEAT.md is "effectively empty" when it has no actionable task lines.
|
|
11
|
-
* Whitespace, ATX headers, and stub checklist items (`- [ ]`) all count as
|
|
12
|
-
* empty so we can skip a turn when the file has been left as a placeholder.
|
|
13
|
-
* Missing content (undefined/null/non-string) returns false — the LLM should
|
|
14
|
-
* still get a chance to act.
|
|
15
|
-
*/
|
|
16
|
-
export function isHeartbeatContentEffectivelyEmpty(content: string | undefined | null): boolean {
|
|
17
|
-
if (typeof content !== 'string') return false
|
|
18
|
-
for (const line of content.split('\n')) {
|
|
19
|
-
const trimmed = line.trim()
|
|
20
|
-
if (!trimmed) continue
|
|
21
|
-
if (/^#+(\s|$)/.test(trimmed)) continue
|
|
22
|
-
if (/^[-*+]\s*(\[[\sXx]?\]\s*)?$/.test(trimmed)) continue
|
|
23
|
-
return false
|
|
24
|
-
}
|
|
25
|
-
return true
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function resolveHeartbeatPrompt(raw?: string | null): string {
|
|
29
|
-
const trimmed = typeof raw === 'string' ? raw.trim() : ''
|
|
30
|
-
return trimmed || HEARTBEAT_PROMPT
|
|
31
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
export type { OAuthAuthInfo, OAuthPrompt } from '@mariozechner/pi-ai'
|
|
2
|
-
export type { StoredCredentials } from './auth/openai-codex.ts'
|
|
3
|
-
export {
|
|
4
|
-
clearCredentials as clearOpenAICodexCredentials,
|
|
5
|
-
getStatus as getOpenAICodexStatus,
|
|
6
|
-
hasCredentials as hasOpenAICodexCredentials,
|
|
7
|
-
loadAccessToken as loadOpenAICodexAccessToken,
|
|
8
|
-
loginOpenAICodex,
|
|
9
|
-
OPENAI_CODEX_SECRET_KEY,
|
|
10
|
-
refreshOpenAICodexToken,
|
|
11
|
-
saveLoginCredentials as saveOpenAICodexLoginCredentials,
|
|
12
|
-
} from './auth/openai-codex.ts'
|
|
13
|
-
export {
|
|
14
|
-
DEFAULT_HEARTBEAT_EVERY_SEC,
|
|
15
|
-
HEARTBEAT_PROMPT,
|
|
16
|
-
isHeartbeatContentEffectivelyEmpty,
|
|
17
|
-
resolveHeartbeatPrompt,
|
|
18
|
-
} from './auto-reply/heartbeat.ts'
|
|
19
|
-
export { filesBackend } from './memory/files.ts'
|
|
20
|
-
export { qmdBackend } from './memory/qmd.ts'
|
|
21
|
-
export type { MemoryBackend } from './memory/types.ts'
|
|
22
|
-
export {
|
|
23
|
-
extractAssistantText,
|
|
24
|
-
extractAssistantToolCalls,
|
|
25
|
-
extractToolResultText,
|
|
26
|
-
piMessagesToProviderView,
|
|
27
|
-
translatePiEvent,
|
|
28
|
-
} from './pi/events.ts'
|
|
29
|
-
export type { BazilionSessionHandle, CreateBazilionSessionOptions } from './pi/session.ts'
|
|
30
|
-
export {
|
|
31
|
-
countSessionMessagesForTest,
|
|
32
|
-
createBazilionSession,
|
|
33
|
-
isProviderEnabled,
|
|
34
|
-
loadEnabledRegistry,
|
|
35
|
-
loadInitialMessages,
|
|
36
|
-
loadSessionHead,
|
|
37
|
-
seedSessionForTest,
|
|
38
|
-
} from './pi/session.ts'
|
|
39
|
-
// Pi-coding-agent integration surface. The worker runs inside a
|
|
40
|
-
// `BazilionSession` (pi's AgentSession wrapper) and endpoints (/compact,
|
|
41
|
-
// /context, /reset) drive pi session operations directly.
|
|
42
|
-
export type { BazilionCustomToolsOpts } from './pi/tools.ts'
|
|
43
|
-
export { createBazilionCustomTools, ourToolToPiTool } from './pi/tools.ts'
|
|
44
|
-
export type { CatalogResult, LiveFetchResult } from './providers/catalog.ts'
|
|
45
|
-
export { listCatalogModels, listCatalogModelsSync } from './providers/catalog.ts'
|
|
46
|
-
export type { PiProviderConfig } from './providers/pi-adapter.ts'
|
|
47
|
-
export { piProvider, resolveModel as resolvePiModel } from './providers/pi-adapter.ts'
|
|
48
|
-
export type {
|
|
49
|
-
ProviderConfig,
|
|
50
|
-
ProviderMeta,
|
|
51
|
-
ProviderRegistry,
|
|
52
|
-
ResolvedModel,
|
|
53
|
-
} from './providers/registry.ts'
|
|
54
|
-
export {
|
|
55
|
-
createProviderRegistry,
|
|
56
|
-
listAllProviders,
|
|
57
|
-
loadProviderConfigFromEnv,
|
|
58
|
-
} from './providers/registry.ts'
|
|
59
|
-
export type { RetryOptions } from './providers/retry.ts'
|
|
60
|
-
export { isRetryableError, withRetry } from './providers/retry.ts'
|
|
61
|
-
export type {
|
|
62
|
-
Provider,
|
|
63
|
-
ProviderRequest,
|
|
64
|
-
ProviderResponse,
|
|
65
|
-
StopReason,
|
|
66
|
-
} from './providers/types.ts'
|
|
67
|
-
export { buildSystemPrompt } from './session/prompt.ts'
|
|
68
|
-
export { bootstrapTool } from './tools/bootstrap.ts'
|
|
69
|
-
export { memoryTools } from './tools/memory.ts'
|
|
70
|
-
export { messagingTools } from './tools/messaging.ts'
|
|
71
|
-
export { createToolRegistry } from './tools/registry.ts'
|
|
72
|
-
export type { ToolHandler, ToolRegistry } from './tools/types.ts'
|
|
73
|
-
export { webTools } from './tools/web.ts'
|
|
74
|
-
|
|
75
|
-
export type { MessagingHost } from './worker/ipc-protocol.ts'
|
|
76
|
-
export type { SpawnWorkerOpts } from './worker/spawn.ts'
|
|
77
|
-
export { spawnWorkerTurn } from './worker/spawn.ts'
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
existsSync,
|
|
3
|
-
mkdirSync,
|
|
4
|
-
readdirSync,
|
|
5
|
-
readFileSync,
|
|
6
|
-
rmSync,
|
|
7
|
-
statSync,
|
|
8
|
-
writeFileSync,
|
|
9
|
-
} from 'node:fs'
|
|
10
|
-
import { dirname, join } from 'node:path'
|
|
11
|
-
import type { MemoryEntry, MemoryHit } from '@bazilion/api-types'
|
|
12
|
-
import type { MemoryBackend } from './types.ts'
|
|
13
|
-
|
|
14
|
-
const SNIPPET_PAD = 60
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Filesystem-backed memory: every entry is a file under `root`. Search is
|
|
18
|
-
* substring matching across all files. Index = the filesystem itself.
|
|
19
|
-
*/
|
|
20
|
-
export function filesBackend(root: string): MemoryBackend {
|
|
21
|
-
function safe(key: string): string {
|
|
22
|
-
if (key.includes('..') || key.startsWith('/') || key.includes('\0')) {
|
|
23
|
-
throw new Error(`unsafe memory key: ${key}`)
|
|
24
|
-
}
|
|
25
|
-
return join(root, key)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function walk(dir: string, prefix: string, out: MemoryEntry[]): void {
|
|
29
|
-
if (!existsSync(dir)) return
|
|
30
|
-
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
31
|
-
const full = join(dir, e.name)
|
|
32
|
-
const key = prefix ? `${prefix}/${e.name}` : e.name
|
|
33
|
-
if (e.isDirectory()) {
|
|
34
|
-
walk(full, key, out)
|
|
35
|
-
} else if (e.isFile()) {
|
|
36
|
-
const stats = statSync(full)
|
|
37
|
-
out.push({
|
|
38
|
-
key,
|
|
39
|
-
content: readFileSync(full, 'utf8'),
|
|
40
|
-
updatedAt: stats.mtimeMs,
|
|
41
|
-
})
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
async init() {
|
|
48
|
-
mkdirSync(root, { recursive: true })
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
async read(key) {
|
|
52
|
-
const path = safe(key)
|
|
53
|
-
if (!existsSync(path)) {
|
|
54
|
-
throw new Error(`memory entry not found: ${key}`)
|
|
55
|
-
}
|
|
56
|
-
const stats = statSync(path)
|
|
57
|
-
return {
|
|
58
|
-
key,
|
|
59
|
-
content: readFileSync(path, 'utf8'),
|
|
60
|
-
updatedAt: stats.mtimeMs,
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
async write(key, content) {
|
|
65
|
-
const path = safe(key)
|
|
66
|
-
mkdirSync(dirname(path), { recursive: true })
|
|
67
|
-
writeFileSync(path, content)
|
|
68
|
-
const stats = statSync(path)
|
|
69
|
-
return { key, content, updatedAt: stats.mtimeMs }
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
async search(query, opts) {
|
|
73
|
-
const limit = opts?.limit ?? 10
|
|
74
|
-
const all: MemoryEntry[] = []
|
|
75
|
-
walk(root, '', all)
|
|
76
|
-
const q = query.toLowerCase()
|
|
77
|
-
const hits: MemoryHit[] = []
|
|
78
|
-
for (const entry of all) {
|
|
79
|
-
const idx = entry.content.toLowerCase().indexOf(q)
|
|
80
|
-
if (idx === -1) continue
|
|
81
|
-
const start = Math.max(0, idx - SNIPPET_PAD)
|
|
82
|
-
const end = Math.min(entry.content.length, idx + query.length + SNIPPET_PAD)
|
|
83
|
-
hits.push({
|
|
84
|
-
key: entry.key,
|
|
85
|
-
snippet: entry.content.slice(start, end),
|
|
86
|
-
score: 1,
|
|
87
|
-
})
|
|
88
|
-
}
|
|
89
|
-
return hits.slice(0, limit)
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
async list() {
|
|
93
|
-
const out: MemoryEntry[] = []
|
|
94
|
-
walk(root, '', out)
|
|
95
|
-
return out.sort((a, b) => a.key.localeCompare(b.key))
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
async remove(key) {
|
|
99
|
-
const path = safe(key)
|
|
100
|
-
if (existsSync(path)) rmSync(path)
|
|
101
|
-
},
|
|
102
|
-
}
|
|
103
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
existsSync,
|
|
3
|
-
mkdirSync,
|
|
4
|
-
readdirSync,
|
|
5
|
-
readFileSync,
|
|
6
|
-
rmSync,
|
|
7
|
-
statSync,
|
|
8
|
-
writeFileSync,
|
|
9
|
-
} from 'node:fs'
|
|
10
|
-
import { dirname, join } from 'node:path'
|
|
11
|
-
import type { MemoryEntry, MemoryHit } from '@bazilion/api-types'
|
|
12
|
-
import { createStore, extractSnippet, type QMDStore } from '@tobilu/qmd'
|
|
13
|
-
import type { MemoryBackend } from './types.ts'
|
|
14
|
-
|
|
15
|
-
const INDEX_FILENAME = '.qmd-index.sqlite'
|
|
16
|
-
const COLLECTION_NAME = 'memory'
|
|
17
|
-
const PATTERN = '**/*.md'
|
|
18
|
-
|
|
19
|
-
// One store per memory directory per process. createStore opens a SQLite
|
|
20
|
-
// handle; we dedupe so concurrent chat requests for the same agent reuse it.
|
|
21
|
-
const storeCache = new Map<string, Promise<QMDStore>>()
|
|
22
|
-
|
|
23
|
-
function getStore(dir: string): Promise<QMDStore> {
|
|
24
|
-
let p = storeCache.get(dir)
|
|
25
|
-
if (!p) {
|
|
26
|
-
p = createStore({
|
|
27
|
-
dbPath: join(dir, INDEX_FILENAME),
|
|
28
|
-
config: {
|
|
29
|
-
collections: {
|
|
30
|
-
[COLLECTION_NAME]: { path: dir, pattern: PATTERN },
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
})
|
|
34
|
-
storeCache.set(dir, p)
|
|
35
|
-
}
|
|
36
|
-
return p
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function safeKey(root: string, key: string): string {
|
|
40
|
-
if (key.includes('..') || key.startsWith('/') || key.includes('\0')) {
|
|
41
|
-
throw new Error(`unsafe memory key: ${key}`)
|
|
42
|
-
}
|
|
43
|
-
return join(root, key)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function walkMd(dir: string, prefix: string, out: MemoryEntry[]): void {
|
|
47
|
-
if (!existsSync(dir)) return
|
|
48
|
-
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
49
|
-
if (e.name.startsWith('.')) continue // skip .qmd-index.sqlite and friends
|
|
50
|
-
const full = join(dir, e.name)
|
|
51
|
-
const key = prefix ? `${prefix}/${e.name}` : e.name
|
|
52
|
-
if (e.isDirectory()) {
|
|
53
|
-
walkMd(full, key, out)
|
|
54
|
-
} else if (e.isFile() && e.name.endsWith('.md')) {
|
|
55
|
-
const stats = statSync(full)
|
|
56
|
-
out.push({
|
|
57
|
-
key,
|
|
58
|
-
content: readFileSync(full, 'utf8'),
|
|
59
|
-
updatedAt: stats.mtimeMs,
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Memory backend backed by @tobilu/qmd — BM25 keyword search over markdown
|
|
67
|
-
* files under `root`. Writes markdown to disk, then asks qmd to reindex.
|
|
68
|
-
*
|
|
69
|
-
* Uses `searchLex` only; no embeddings, no LLM rerank, no model download.
|
|
70
|
-
* The hybrid `search()` / `searchVector()` paths exist in the qmd SDK and
|
|
71
|
-
* can be wired in later if we want semantic search — they'd add a dependency
|
|
72
|
-
* on `node-llama-cpp` and several GB of GGUF models.
|
|
73
|
-
*/
|
|
74
|
-
export function qmdBackend(root: string): MemoryBackend {
|
|
75
|
-
return {
|
|
76
|
-
async init() {
|
|
77
|
-
mkdirSync(root, { recursive: true })
|
|
78
|
-
// Opening the store + initial scan. update() is idempotent.
|
|
79
|
-
const store = await getStore(root)
|
|
80
|
-
await store.update()
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
async read(key) {
|
|
84
|
-
const path = safeKey(root, key)
|
|
85
|
-
if (!existsSync(path)) {
|
|
86
|
-
throw new Error(`memory entry not found: ${key}`)
|
|
87
|
-
}
|
|
88
|
-
const stats = statSync(path)
|
|
89
|
-
return {
|
|
90
|
-
key,
|
|
91
|
-
content: readFileSync(path, 'utf8'),
|
|
92
|
-
updatedAt: stats.mtimeMs,
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
async write(key, content) {
|
|
97
|
-
const path = safeKey(root, key)
|
|
98
|
-
mkdirSync(dirname(path), { recursive: true })
|
|
99
|
-
writeFileSync(path, content)
|
|
100
|
-
const stats = statSync(path)
|
|
101
|
-
// Reindex so the newly-written file is searchable on the next search().
|
|
102
|
-
// update() re-scans the collection; for typical memory sizes (tens of
|
|
103
|
-
// files) this is sub-millisecond.
|
|
104
|
-
const store = await getStore(root)
|
|
105
|
-
await store.update()
|
|
106
|
-
return { key, content, updatedAt: stats.mtimeMs }
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
async search(query, opts) {
|
|
110
|
-
const limit = opts?.limit ?? 10
|
|
111
|
-
const store = await getStore(root)
|
|
112
|
-
const results = await store.searchLex(query, {
|
|
113
|
-
limit,
|
|
114
|
-
collection: COLLECTION_NAME,
|
|
115
|
-
})
|
|
116
|
-
const hits: MemoryHit[] = []
|
|
117
|
-
for (const r of results) {
|
|
118
|
-
// qmd's filepath is a synthetic URI (`qmd://<collection>/<path>`);
|
|
119
|
-
// displayPath is collection-prefixed (`<collection>/<path>`). Strip
|
|
120
|
-
// the leading `<collection>/` to get the key the caller wrote.
|
|
121
|
-
const prefix = `${COLLECTION_NAME}/`
|
|
122
|
-
const key = r.displayPath.startsWith(prefix)
|
|
123
|
-
? r.displayPath.slice(prefix.length)
|
|
124
|
-
: r.displayPath
|
|
125
|
-
let content = r.body ?? ''
|
|
126
|
-
if (!content) {
|
|
127
|
-
try {
|
|
128
|
-
content = readFileSync(join(root, key), 'utf8')
|
|
129
|
-
} catch {
|
|
130
|
-
content = ''
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
const snippet = extractSnippet(content, query).snippet
|
|
134
|
-
hits.push({ key, snippet, score: r.score })
|
|
135
|
-
}
|
|
136
|
-
return hits
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
async list() {
|
|
140
|
-
const out: MemoryEntry[] = []
|
|
141
|
-
walkMd(root, '', out)
|
|
142
|
-
return out.sort((a, b) => a.key.localeCompare(b.key))
|
|
143
|
-
},
|
|
144
|
-
|
|
145
|
-
async remove(key) {
|
|
146
|
-
const path = safeKey(root, key)
|
|
147
|
-
if (existsSync(path)) rmSync(path)
|
|
148
|
-
const store = await getStore(root)
|
|
149
|
-
await store.update()
|
|
150
|
-
},
|
|
151
|
-
}
|
|
152
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { MemoryEntry, MemoryHit } from '@bazilion/api-types'
|
|
2
|
-
|
|
3
|
-
export interface MemoryBackend {
|
|
4
|
-
/** Initialize storage. Idempotent. */
|
|
5
|
-
init(): Promise<void>
|
|
6
|
-
/** Read a memory entry by key. Throws if missing. */
|
|
7
|
-
read(key: string): Promise<MemoryEntry>
|
|
8
|
-
/** Write or overwrite a memory entry. */
|
|
9
|
-
write(key: string, content: string): Promise<MemoryEntry>
|
|
10
|
-
/** Search memory for matches against a query string. */
|
|
11
|
-
search(query: string, opts?: { limit?: number }): Promise<MemoryHit[]>
|
|
12
|
-
/** List all entries (sorted by key). */
|
|
13
|
-
list(): Promise<MemoryEntry[]>
|
|
14
|
-
/** Delete an entry. No-op if missing. */
|
|
15
|
-
remove(key: string): Promise<void>
|
|
16
|
-
}
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
// Translator: pi `AgentSessionEvent` → Bazilion `SessionEvent[]`.
|
|
2
|
-
//
|
|
3
|
-
// The worker's NDJSON wire format (`ChatFrame`) is still the same discrete
|
|
4
|
-
// event stream CLI / browser clients know how to render. This module is the
|
|
5
|
-
// adapter at the single choke-point — pi event in, zero-or-more Bazilion
|
|
6
|
-
// events out.
|
|
7
|
-
//
|
|
8
|
-
// Coverage:
|
|
9
|
-
// - user message_start → one `user_message`
|
|
10
|
-
// - assistant text_delta → one `assistant_delta` per chunk
|
|
11
|
-
// - assistant message_end:
|
|
12
|
-
// - if the message carries text → one `assistant_message`
|
|
13
|
-
// - for every tool-call content → one `tool_call`
|
|
14
|
-
// - tool_execution_end → `tool_result` or `tool_error` based on isError
|
|
15
|
-
// - message_end with aborted/error stopReason → one `error`
|
|
16
|
-
//
|
|
17
|
-
// Not surfaced (intentional):
|
|
18
|
-
// - agent_start / agent_end — run-row lifecycle, not chat events
|
|
19
|
-
// - turn_start / turn_end — too chatty, nothing to render
|
|
20
|
-
// - message_start (assistant) — text will come via updates + end
|
|
21
|
-
// - message_update (non-text) — thinking deltas etc; left for a later
|
|
22
|
-
// pass when UIs render thinking blocks
|
|
23
|
-
// - queue_update / compaction_* — session meta, not assistant output
|
|
24
|
-
// - auto_retry_* — silently retried, user sees only the
|
|
25
|
-
// eventual success or failure
|
|
26
|
-
|
|
27
|
-
import type { ProviderMessage, SessionEvent, ToolCall } from '@bazilion/api-types'
|
|
28
|
-
import type { AgentMessage, AgentToolResult } from '@mariozechner/pi-agent-core'
|
|
29
|
-
import type { AssistantMessage } from '@mariozechner/pi-ai'
|
|
30
|
-
import type { AgentSessionEvent } from '@mariozechner/pi-coding-agent'
|
|
31
|
-
|
|
32
|
-
export function translatePiEvent(e: AgentSessionEvent): SessionEvent[] {
|
|
33
|
-
switch (e.type) {
|
|
34
|
-
case 'message_start': {
|
|
35
|
-
if (e.message.role === 'user') {
|
|
36
|
-
return [{ type: 'user_message', text: stringifyContent(e.message.content) }]
|
|
37
|
-
}
|
|
38
|
-
return []
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
case 'message_update': {
|
|
42
|
-
const inner = e.assistantMessageEvent
|
|
43
|
-
if (inner.type === 'text_delta') {
|
|
44
|
-
return [{ type: 'assistant_delta', delta: inner.delta }]
|
|
45
|
-
}
|
|
46
|
-
return []
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
case 'message_end': {
|
|
50
|
-
const m = e.message
|
|
51
|
-
if (m.role !== 'assistant') return []
|
|
52
|
-
const out: SessionEvent[] = []
|
|
53
|
-
const text = extractAssistantText(m as AssistantMessage)
|
|
54
|
-
if (text) out.push({ type: 'assistant_message', text })
|
|
55
|
-
for (const block of (m as AssistantMessage).content ?? []) {
|
|
56
|
-
if (block.type === 'toolCall') {
|
|
57
|
-
out.push({
|
|
58
|
-
type: 'tool_call',
|
|
59
|
-
id: block.id,
|
|
60
|
-
name: block.name,
|
|
61
|
-
arguments: JSON.stringify(block.arguments ?? {}),
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
const stopReason = (m as AssistantMessage).stopReason
|
|
66
|
-
if (stopReason === 'aborted' || stopReason === 'error') {
|
|
67
|
-
const errText = (m as AssistantMessage).errorMessage ?? stopReason
|
|
68
|
-
out.push({ type: 'error', error: errText })
|
|
69
|
-
}
|
|
70
|
-
return out
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
case 'tool_execution_end': {
|
|
74
|
-
const text = extractToolResultText(e.result)
|
|
75
|
-
if (e.isError) {
|
|
76
|
-
return [{ type: 'tool_error', id: e.toolCallId, name: e.toolName, error: text }]
|
|
77
|
-
}
|
|
78
|
-
return [{ type: 'tool_result', id: e.toolCallId, name: e.toolName, result: text }]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
default:
|
|
82
|
-
return []
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function extractAssistantText(m: AssistantMessage): string {
|
|
87
|
-
let out = ''
|
|
88
|
-
for (const block of m.content ?? []) {
|
|
89
|
-
if (block.type === 'text') out += block.text
|
|
90
|
-
}
|
|
91
|
-
return out
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function extractAssistantToolCalls(m: AssistantMessage): ToolCall[] {
|
|
95
|
-
const out: ToolCall[] = []
|
|
96
|
-
for (const block of m.content ?? []) {
|
|
97
|
-
if (block.type === 'toolCall') {
|
|
98
|
-
out.push({ id: block.id, name: block.name, arguments: JSON.stringify(block.arguments ?? {}) })
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return out
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** Flatten `AgentToolResult.content` blocks into a single string — matches the
|
|
105
|
-
* shape Bazilion tools always returned before pi adoption. */
|
|
106
|
-
export function extractToolResultText(result: unknown): string {
|
|
107
|
-
const r = result as AgentToolResult<unknown> | undefined
|
|
108
|
-
if (!r?.content) return ''
|
|
109
|
-
let out = ''
|
|
110
|
-
for (const block of r.content) {
|
|
111
|
-
if (block.type === 'text') out += block.text
|
|
112
|
-
}
|
|
113
|
-
return out
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** Stringify a pi user-message content array. */
|
|
117
|
-
function stringifyContent(content: unknown): string {
|
|
118
|
-
if (typeof content === 'string') return content
|
|
119
|
-
if (!Array.isArray(content)) return ''
|
|
120
|
-
let out = ''
|
|
121
|
-
for (const block of content as { type: string; text?: string }[]) {
|
|
122
|
-
if (block.type === 'text' && typeof block.text === 'string') out += block.text
|
|
123
|
-
}
|
|
124
|
-
return out
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Convert pi's authoritative `session.state.messages` (AgentMessage[]) into
|
|
129
|
-
* Bazilion's ProviderMessage[] shape, so the `done` ChatFrame stays
|
|
130
|
-
* compatible with browser + CLI clients that rehydrate from it.
|
|
131
|
-
*
|
|
132
|
-
* Notes on role mapping: pi uses `"toolResult"` for tool-response messages;
|
|
133
|
-
* Bazilion uses `"tool"`. AssistantMessage content arrays become either
|
|
134
|
-
* `content` string (text blocks joined) or a `toolCalls` array (toolCall
|
|
135
|
-
* blocks converted).
|
|
136
|
-
*/
|
|
137
|
-
export function piMessagesToProviderView(messages: AgentMessage[]): ProviderMessage[] {
|
|
138
|
-
const out: ProviderMessage[] = []
|
|
139
|
-
for (const m of messages) {
|
|
140
|
-
// Custom Bazilion message types would land in this switch too, but we
|
|
141
|
-
// don't register any via CustomAgentMessages declaration merging yet.
|
|
142
|
-
switch (m.role) {
|
|
143
|
-
case 'user': {
|
|
144
|
-
out.push({ role: 'user', content: stringifyContent((m as { content: unknown }).content) })
|
|
145
|
-
break
|
|
146
|
-
}
|
|
147
|
-
case 'assistant': {
|
|
148
|
-
const am = m as AssistantMessage
|
|
149
|
-
const text = extractAssistantText(am)
|
|
150
|
-
const toolCalls = extractAssistantToolCalls(am)
|
|
151
|
-
const msg: ProviderMessage = { role: 'assistant', content: text }
|
|
152
|
-
if (toolCalls.length > 0) msg.toolCalls = toolCalls
|
|
153
|
-
out.push(msg)
|
|
154
|
-
break
|
|
155
|
-
}
|
|
156
|
-
case 'toolResult': {
|
|
157
|
-
const tr = m as { content: unknown; toolCallId?: string; toolName?: string }
|
|
158
|
-
out.push({
|
|
159
|
-
role: 'tool',
|
|
160
|
-
content: stringifyContent(tr.content),
|
|
161
|
-
toolCallId: tr.toolCallId,
|
|
162
|
-
toolName: tr.toolName,
|
|
163
|
-
})
|
|
164
|
-
break
|
|
165
|
-
}
|
|
166
|
-
default:
|
|
167
|
-
// System and other unknown roles are skipped — the runtime's system
|
|
168
|
-
// prompt is already wired via pi's settingsManager + our buildSystemPrompt.
|
|
169
|
-
break
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return out
|
|
173
|
-
}
|