agent-mockingbird 0.0.1
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/.agents/skills/btca-cli/SKILL.md +64 -0
- package/.agents/skills/btca-cli/agents/openai.yaml +3 -0
- package/.agents/skills/frontend-design/SKILL.md +42 -0
- package/.agents/skills/frontend-design/agents/openai.yaml +3 -0
- package/.env.example +36 -0
- package/.githooks/pre-commit +33 -0
- package/.github/workflows/ci.yml +309 -0
- package/.opencode/bun.lock +18 -0
- package/.opencode/package.json +5 -0
- package/.opencode/tools/agent_type_manager.ts +100 -0
- package/.opencode/tools/config_manager.ts +87 -0
- package/.opencode/tools/cron_manager.ts +145 -0
- package/.opencode/tools/memory_get.ts +43 -0
- package/.opencode/tools/memory_remember.ts +53 -0
- package/.opencode/tools/memory_search.ts +48 -0
- package/AGENTS.md +126 -0
- package/MEMORY.md +2 -0
- package/README.md +451 -0
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/agent-mockingbird.config.example.json +135 -0
- package/apps/server/package.json +32 -0
- package/apps/server/src/backend/agents/bootstrapContext.ts +362 -0
- package/apps/server/src/backend/agents/openclawImport.test.ts +133 -0
- package/apps/server/src/backend/agents/openclawImport.ts +797 -0
- package/apps/server/src/backend/agents/opencodeConfig.ts +428 -0
- package/apps/server/src/backend/agents/service.ts +10 -0
- package/apps/server/src/backend/config/example-config.test.ts +20 -0
- package/apps/server/src/backend/config/orchestration.ts +243 -0
- package/apps/server/src/backend/config/policy.ts +158 -0
- package/apps/server/src/backend/config/schema.test.ts +15 -0
- package/apps/server/src/backend/config/schema.ts +391 -0
- package/apps/server/src/backend/config/semantic.test.ts +34 -0
- package/apps/server/src/backend/config/semantic.ts +149 -0
- package/apps/server/src/backend/config/service.test.ts +75 -0
- package/apps/server/src/backend/config/service.ts +207 -0
- package/apps/server/src/backend/config/smoke.ts +77 -0
- package/apps/server/src/backend/config/store.test.ts +123 -0
- package/apps/server/src/backend/config/store.ts +581 -0
- package/apps/server/src/backend/config/testFixtures.ts +5 -0
- package/apps/server/src/backend/config/types.ts +56 -0
- package/apps/server/src/backend/contracts/events.ts +320 -0
- package/apps/server/src/backend/contracts/runtime.ts +111 -0
- package/apps/server/src/backend/cron/executor.ts +435 -0
- package/apps/server/src/backend/cron/repository.ts +170 -0
- package/apps/server/src/backend/cron/service.ts +660 -0
- package/apps/server/src/backend/cron/storage.ts +92 -0
- package/apps/server/src/backend/cron/types.ts +138 -0
- package/apps/server/src/backend/cron/utils.ts +351 -0
- package/apps/server/src/backend/db/client.ts +20 -0
- package/apps/server/src/backend/db/migrate.ts +40 -0
- package/apps/server/src/backend/db/repository.ts +1762 -0
- package/apps/server/src/backend/db/schema.ts +113 -0
- package/apps/server/src/backend/db/usageDashboard.test.ts +102 -0
- package/apps/server/src/backend/db/wipe.ts +13 -0
- package/apps/server/src/backend/defaults.ts +32 -0
- package/apps/server/src/backend/env.ts +48 -0
- package/apps/server/src/backend/heartbeat/activeHours.ts +45 -0
- package/apps/server/src/backend/heartbeat/defaultJob.ts +88 -0
- package/apps/server/src/backend/heartbeat/heartbeat.test.ts +110 -0
- package/apps/server/src/backend/heartbeat/runtimeService.ts +190 -0
- package/apps/server/src/backend/heartbeat/service.ts +176 -0
- package/apps/server/src/backend/heartbeat/state.test.ts +63 -0
- package/apps/server/src/backend/heartbeat/state.ts +167 -0
- package/apps/server/src/backend/heartbeat/types.ts +54 -0
- package/apps/server/src/backend/http/boundedQueue.test.ts +49 -0
- package/apps/server/src/backend/http/boundedQueue.ts +92 -0
- package/apps/server/src/backend/http/parsers.ts +40 -0
- package/apps/server/src/backend/http/router.ts +61 -0
- package/apps/server/src/backend/http/routes/agentRoutes.ts +67 -0
- package/apps/server/src/backend/http/routes/backgroundRoutes.ts +203 -0
- package/apps/server/src/backend/http/routes/chatRoutes.ts +107 -0
- package/apps/server/src/backend/http/routes/configRoutes.ts +602 -0
- package/apps/server/src/backend/http/routes/cronRoutes.ts +221 -0
- package/apps/server/src/backend/http/routes/dashboardRoutes.ts +308 -0
- package/apps/server/src/backend/http/routes/eventRoutes.ts +7 -0
- package/apps/server/src/backend/http/routes/heartbeatRoutes.test.ts +41 -0
- package/apps/server/src/backend/http/routes/heartbeatRoutes.ts +28 -0
- package/apps/server/src/backend/http/routes/index.ts +101 -0
- package/apps/server/src/backend/http/routes/mcpRoutes.ts +213 -0
- package/apps/server/src/backend/http/routes/memoryRoutes.ts +154 -0
- package/apps/server/src/backend/http/routes/runRoutes.ts +310 -0
- package/apps/server/src/backend/http/routes/runtimeRoutes.ts +197 -0
- package/apps/server/src/backend/http/routes/skillRoutes.ts +112 -0
- package/apps/server/src/backend/http/routes/uiRoutes.test.ts +161 -0
- package/apps/server/src/backend/http/routes/uiRoutes.ts +177 -0
- package/apps/server/src/backend/http/routes/usageRoutes.test.ts +104 -0
- package/apps/server/src/backend/http/routes/usageRoutes.ts +767 -0
- package/apps/server/src/backend/http/schemas.ts +64 -0
- package/apps/server/src/backend/http/sse.ts +144 -0
- package/apps/server/src/backend/integration/backend-core.test.ts +2316 -0
- package/apps/server/src/backend/logging/logger.ts +64 -0
- package/apps/server/src/backend/mcp/service.ts +326 -0
- package/apps/server/src/backend/memory/cli.ts +170 -0
- package/apps/server/src/backend/memory/conceptExpansion.test.ts +28 -0
- package/apps/server/src/backend/memory/conceptExpansion.ts +80 -0
- package/apps/server/src/backend/memory/qmdPort.test.ts +54 -0
- package/apps/server/src/backend/memory/qmdPort.ts +61 -0
- package/apps/server/src/backend/memory/records.test.ts +66 -0
- package/apps/server/src/backend/memory/records.ts +229 -0
- package/apps/server/src/backend/memory/service.ts +2012 -0
- package/apps/server/src/backend/memory/sqliteVec.ts +58 -0
- package/apps/server/src/backend/memory/types.ts +104 -0
- package/apps/server/src/backend/opencode/agentMockingbirdPlugin.test.ts +396 -0
- package/apps/server/src/backend/opencode/client.ts +98 -0
- package/apps/server/src/backend/opencode/models.ts +41 -0
- package/apps/server/src/backend/opencode/systemPrompt.test.ts +146 -0
- package/apps/server/src/backend/opencode/systemPrompt.ts +284 -0
- package/apps/server/src/backend/paths.ts +57 -0
- package/apps/server/src/backend/prompts/service.ts +100 -0
- package/apps/server/src/backend/queue/queue.test.ts +189 -0
- package/apps/server/src/backend/queue/service.ts +177 -0
- package/apps/server/src/backend/queue/types.ts +39 -0
- package/apps/server/src/backend/run/service.ts +576 -0
- package/apps/server/src/backend/run/storage.ts +47 -0
- package/apps/server/src/backend/run/types.ts +44 -0
- package/apps/server/src/backend/runtime/errors.ts +61 -0
- package/apps/server/src/backend/runtime/index.ts +72 -0
- package/apps/server/src/backend/runtime/memoryPromptDedup.test.ts +153 -0
- package/apps/server/src/backend/runtime/memoryPromptDedup.ts +76 -0
- package/apps/server/src/backend/runtime/opencodeRuntime/backgroundMethods.ts +765 -0
- package/apps/server/src/backend/runtime/opencodeRuntime/coreMethods.ts +705 -0
- package/apps/server/src/backend/runtime/opencodeRuntime/eventMethods.ts +503 -0
- package/apps/server/src/backend/runtime/opencodeRuntime/memoryMethods.ts +462 -0
- package/apps/server/src/backend/runtime/opencodeRuntime/promptMethods.ts +1167 -0
- package/apps/server/src/backend/runtime/opencodeRuntime/shared.ts +254 -0
- package/apps/server/src/backend/runtime/opencodeRuntime.test.ts +2899 -0
- package/apps/server/src/backend/runtime/opencodeRuntime.ts +135 -0
- package/apps/server/src/backend/runtime/sessionScope.ts +45 -0
- package/apps/server/src/backend/skills/service.ts +442 -0
- package/apps/server/src/backend/workspace/resolve.ts +27 -0
- package/apps/server/src/cli/agent-mockingbird.mjs +2522 -0
- package/apps/server/src/cli/agent-mockingbird.test.ts +68 -0
- package/apps/server/src/cli/runtime-assets.mjs +269 -0
- package/apps/server/src/cli/runtime-assets.test.ts +52 -0
- package/apps/server/src/cli/runtime-layout.mjs +75 -0
- package/apps/server/src/cli/standaloneBuild.test.ts +19 -0
- package/apps/server/src/cli/standaloneBuild.ts +19 -0
- package/apps/server/src/cli/standaloneCronBinary.test.ts +187 -0
- package/apps/server/src/index.ts +178 -0
- package/apps/server/tsconfig.json +12 -0
- package/backlog.md +5 -0
- package/bin/agent-mockingbird +2522 -0
- package/bin/runtime-layout.mjs +75 -0
- package/build-bin.ts +34 -0
- package/build-cli.mjs +37 -0
- package/build.ts +40 -0
- package/bun-env.d.ts +11 -0
- package/bun.lock +888 -0
- package/bunfig.toml +2 -0
- package/components.json +21 -0
- package/config.json +130 -0
- package/deploy/RELEASE_INSTALL.md +112 -0
- package/deploy/docker-compose.yml +42 -0
- package/deploy/systemd/README.md +46 -0
- package/deploy/systemd/agent-mockingbird.service +28 -0
- package/deploy/systemd/opencode.service +25 -0
- package/docs/legacy-config-ui-reference.md +51 -0
- package/docs/memory-e2e-trace-2026-03-04.md +63 -0
- package/docs/memory-ops.md +96 -0
- package/docs/memory-runtime-contract.md +42 -0
- package/docs/memory-tuning-remote-2026-03-04.md +59 -0
- package/docs/opencode-rebase-workflow-plan.md +614 -0
- package/docs/opencode-startup-sync-plan.md +94 -0
- package/docs/vendor-opencode.md +41 -0
- package/drizzle/0000_famous_turbo.sql +49 -0
- package/drizzle/0001_cron_memory_aux.sql +160 -0
- package/drizzle/0002_runtime_session_bindings.sql +28 -0
- package/drizzle/0003_background_runs.sql +27 -0
- package/drizzle/0004_memory_open_write.sql +63 -0
- package/drizzle/0005_signal_channel.sql +47 -0
- package/drizzle/0006_usage_event_dimensions.sql +7 -0
- package/drizzle/meta/0000_snapshot.json +341 -0
- package/drizzle/meta/_journal.json +55 -0
- package/drizzle.config.ts +14 -0
- package/eslint.config.mjs +77 -0
- package/knip.json +18 -0
- package/memory/2026-03-04.md +4 -0
- package/opencode.lock.json +16 -0
- package/package.json +67 -0
- package/packages/agent-mockingbird-installer/README.md +31 -0
- package/packages/agent-mockingbird-installer/bin/agent-mockingbird-installer.mjs +44 -0
- package/packages/agent-mockingbird-installer/opencode.lock.json +16 -0
- package/packages/agent-mockingbird-installer/package.json +23 -0
- package/packages/contracts/package.json +19 -0
- package/packages/contracts/src/agentTypes.ts +122 -0
- package/packages/contracts/src/cron.ts +146 -0
- package/packages/contracts/src/dashboard.ts +378 -0
- package/packages/contracts/src/index.ts +3 -0
- package/packages/contracts/tsconfig.json +4 -0
- package/patches/opencode/0001-Wafflebot-OpenCode-baseline.patch +2341 -0
- package/patches/opencode/0002-Fix-OpenCode-web-entry-and-settings-icons.patch +104 -0
- package/patches/opencode/0003-fix-app-remove-duplicate-sidebar-mount.patch +32 -0
- package/patches/opencode/0004-Add-heartbeat-settings-and-usage-nav.patch +506 -0
- package/patches/opencode/0005-Use-chart-icon-for-usage-nav.patch +38 -0
- package/patches/opencode/0006-Modernize-cron-settings.patch +399 -0
- package/patches/opencode/0007-Rename-waffle-namespaces-to-mockingbird.patch +1110 -0
- package/patches/opencode/0008-Remove-cron-contract-section.patch +178 -0
- package/patches/opencode/0009-Rework-cron-tab-as-operations-console.patch +414 -0
- package/patches/opencode/0010-Refine-heartbeat-settings-controls.patch +208 -0
- package/runtime-assets/opencode-config/opencode.jsonc +25 -0
- package/runtime-assets/opencode-config/package.json +5 -0
- package/runtime-assets/opencode-config/plugins/agent-mockingbird.ts +715 -0
- package/runtime-assets/workspace/.agents/skills/config-auditor/SKILL.md +25 -0
- package/runtime-assets/workspace/.agents/skills/config-editor/SKILL.md +24 -0
- package/runtime-assets/workspace/.agents/skills/cron-manager/SKILL.md +57 -0
- package/runtime-assets/workspace/.agents/skills/memory-ops/SKILL.md +120 -0
- package/runtime-assets/workspace/.agents/skills/runtime-diagnose/SKILL.md +25 -0
- package/runtime-assets/workspace/AGENTS.md +56 -0
- package/runtime-assets/workspace/MEMORY.md +4 -0
- package/scripts/build-release-bundle.sh +66 -0
- package/scripts/check-ship.ts +383 -0
- package/scripts/dev-opencode.sh +17 -0
- package/scripts/dev-stack-opencode.sh +15 -0
- package/scripts/dev-stack.sh +61 -0
- package/scripts/install-systemd.sh +87 -0
- package/scripts/memory-e2e.sh +76 -0
- package/scripts/memory-trace-e2e.sh +141 -0
- package/scripts/migrate-opencode-env.ts +108 -0
- package/scripts/onboard/bootstrap.sh +32 -0
- package/scripts/opencode-swap.ts +78 -0
- package/scripts/opencode-sync.ts +715 -0
- package/scripts/runtime-assets-sync.mjs +83 -0
- package/scripts/setup-git-hooks.ts +39 -0
- package/tsconfig.json +45 -0
- package/tui.json +98 -0
- package/turbo.json +36 -0
- package/vendor/OPENCODE_VENDOR.md +13 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
import { tool, type Plugin, type ToolContext } from "@opencode-ai/plugin"
|
|
2
|
+
|
|
3
|
+
const z = tool.schema
|
|
4
|
+
|
|
5
|
+
type JsonObject = Record<string, unknown>
|
|
6
|
+
|
|
7
|
+
type JsonSchema = {
|
|
8
|
+
type?: string
|
|
9
|
+
description?: string
|
|
10
|
+
properties?: Record<string, JsonSchema>
|
|
11
|
+
items?: JsonSchema
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function resolveApiBaseUrl(...envKeys: string[]) {
|
|
15
|
+
for (const key of envKeys) {
|
|
16
|
+
const value = process.env[key]?.trim()
|
|
17
|
+
if (value) return value.replace(/\/+$/, "")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const port = process.env.AGENT_MOCKINGBIRD_PORT?.trim() || process.env.PORT?.trim() || "3001"
|
|
21
|
+
return `http://127.0.0.1:${port}`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function requestJson(pathname: string, init?: RequestInit) {
|
|
25
|
+
const response = await fetch(`${resolveApiBaseUrl(
|
|
26
|
+
"AGENT_MOCKINGBIRD_CONFIG_API_BASE_URL",
|
|
27
|
+
"AGENT_MOCKINGBIRD_MEMORY_API_BASE_URL",
|
|
28
|
+
"AGENT_MOCKINGBIRD_CRON_API_BASE_URL",
|
|
29
|
+
)}${pathname}`, init)
|
|
30
|
+
const payload = (await response.json()) as JsonObject
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
const error = typeof payload.error === "string" ? payload.error : `Request failed (${response.status})`
|
|
33
|
+
throw new Error(error)
|
|
34
|
+
}
|
|
35
|
+
return payload
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const systemPromptCache = {
|
|
39
|
+
value: "",
|
|
40
|
+
expiresAtMs: 0,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const compactionContextCache = {
|
|
44
|
+
prompt: "",
|
|
45
|
+
value: [] as string[],
|
|
46
|
+
expiresAtMs: 0,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type CompactionPayload = {
|
|
50
|
+
prompt?: string
|
|
51
|
+
context: string[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type SessionScope = {
|
|
55
|
+
localSessionId: string | null
|
|
56
|
+
isMain: boolean
|
|
57
|
+
kind: "main" | "cron" | "heartbeat" | "other"
|
|
58
|
+
heartbeat: boolean
|
|
59
|
+
cronJobId: string | null
|
|
60
|
+
cronJobName: string | null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const sessionScopeCache = new Map<string, { value: SessionScope; expiresAtMs: number }>()
|
|
64
|
+
|
|
65
|
+
async function postJson(pathname: string, body: unknown, envKeys: string[] = []) {
|
|
66
|
+
const response = await fetch(`${resolveApiBaseUrl(...envKeys)}${pathname}`, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers: { "Content-Type": "application/json" },
|
|
69
|
+
body: JSON.stringify(body),
|
|
70
|
+
})
|
|
71
|
+
const payload = (await response.json()) as JsonObject
|
|
72
|
+
return {
|
|
73
|
+
ok: response.ok,
|
|
74
|
+
status: response.status,
|
|
75
|
+
payload,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function toPreview(snippet: string) {
|
|
80
|
+
const compact = snippet
|
|
81
|
+
.replace(/^###\s+\[memory:[^\n]+\]\n?/i, "")
|
|
82
|
+
.replace(/^meta:[^\n]*\n?/i, "")
|
|
83
|
+
.trim()
|
|
84
|
+
if (compact.length <= 280) return compact
|
|
85
|
+
return `${compact.slice(0, 280).trimEnd()}...`
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function applyPropertyDescription(parameters: unknown, propertyName: string, description: string) {
|
|
89
|
+
if (!parameters || typeof parameters !== "object") return
|
|
90
|
+
const schema = parameters as JsonSchema
|
|
91
|
+
const property = schema.properties?.[propertyName]
|
|
92
|
+
if (!property) return
|
|
93
|
+
property.description = description
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const toolDescriptionOverrides: Record<string, string> = {
|
|
97
|
+
question:
|
|
98
|
+
"Ask the user a short structured question when multiple-choice or explicit clarification will unblock the assistant faster than plain text.",
|
|
99
|
+
task:
|
|
100
|
+
"Delegate a bounded subtask to a specialized agent when parallel work or focused expertise will help the assistant move faster.",
|
|
101
|
+
bash:
|
|
102
|
+
"Run a shell command in the workspace when direct terminal execution is the fastest way to inspect, verify, or change something.",
|
|
103
|
+
read:
|
|
104
|
+
"Read a file or directory directly when the assistant needs exact local context before acting.",
|
|
105
|
+
write:
|
|
106
|
+
"Create a new file when the assistant needs to add fresh workspace content.",
|
|
107
|
+
edit:
|
|
108
|
+
"Make targeted edits to an existing file when a focused change is enough.",
|
|
109
|
+
apply_patch:
|
|
110
|
+
"Apply a precise patch to workspace files when the assistant needs controlled code or text edits.",
|
|
111
|
+
list:
|
|
112
|
+
"List files in a directory to quickly inspect local workspace structure.",
|
|
113
|
+
glob:
|
|
114
|
+
"Find files by path pattern when the assistant knows roughly where something should live.",
|
|
115
|
+
grep:
|
|
116
|
+
"Search local file contents for exact text or patterns when identifying relevant code or documents.",
|
|
117
|
+
websearch:
|
|
118
|
+
"Search the web for current external information when local context is insufficient and recency matters.",
|
|
119
|
+
webfetch:
|
|
120
|
+
"Fetch and read a specific URL when the assistant already knows which external page or API response is needed.",
|
|
121
|
+
todoread:
|
|
122
|
+
"Read the assistant todo list to understand current planned work.",
|
|
123
|
+
todowrite:
|
|
124
|
+
"Update the assistant todo list when tracking multi-step work would keep execution organized.",
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function rewriteToolDefinition(toolID: string, output: { description: string; parameters: unknown }) {
|
|
128
|
+
const description = toolDescriptionOverrides[toolID]
|
|
129
|
+
if (description) {
|
|
130
|
+
output.description = description
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (toolID === "question") {
|
|
134
|
+
applyPropertyDescription(
|
|
135
|
+
output.parameters,
|
|
136
|
+
"questions",
|
|
137
|
+
"One or more short user-facing questions to ask when structured clarification is needed.",
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (toolID === "task") {
|
|
142
|
+
applyPropertyDescription(output.parameters, "description", "A short summary of the delegated subtask.")
|
|
143
|
+
applyPropertyDescription(output.parameters, "prompt", "Exact instructions for the specialized agent to complete.")
|
|
144
|
+
applyPropertyDescription(
|
|
145
|
+
output.parameters,
|
|
146
|
+
"subagent_type",
|
|
147
|
+
"The specialist agent type that should handle this delegated subtask.",
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (toolID === "bash") {
|
|
152
|
+
applyPropertyDescription(output.parameters, "description", "Short explanation of why this command is being run.")
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function fetchSystemPrompt() {
|
|
157
|
+
const now = Date.now()
|
|
158
|
+
if (systemPromptCache.expiresAtMs > now) {
|
|
159
|
+
return systemPromptCache.value
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const payload = await requestJson("/api/mockingbird/runtime/system-prompt")
|
|
163
|
+
const system = typeof payload.system === "string" ? payload.system : ""
|
|
164
|
+
systemPromptCache.value = system
|
|
165
|
+
systemPromptCache.expiresAtMs = now + 5_000
|
|
166
|
+
return system
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function fetchCompactionContext(sessionID?: string): Promise<CompactionPayload> {
|
|
170
|
+
const now = Date.now()
|
|
171
|
+
const cacheKey = sessionID?.trim() || "__default__"
|
|
172
|
+
if (compactionContextCache.expiresAtMs > now && cacheKey === "__default__") {
|
|
173
|
+
return {
|
|
174
|
+
prompt: compactionContextCache.prompt || undefined,
|
|
175
|
+
context: compactionContextCache.value,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const search = sessionID?.trim() ? `?sessionId=${encodeURIComponent(sessionID.trim())}` : ""
|
|
180
|
+
const payload = await requestJson(`/api/mockingbird/runtime/compaction-context${search}`)
|
|
181
|
+
const prompt = typeof payload.prompt === "string" && payload.prompt.trim().length > 0 ? payload.prompt : undefined
|
|
182
|
+
const context = Array.isArray(payload.context)
|
|
183
|
+
? payload.context.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0)
|
|
184
|
+
: []
|
|
185
|
+
if (cacheKey === "__default__") {
|
|
186
|
+
compactionContextCache.prompt = prompt ?? ""
|
|
187
|
+
compactionContextCache.value = context
|
|
188
|
+
compactionContextCache.expiresAtMs = now + 5_000
|
|
189
|
+
}
|
|
190
|
+
return { prompt, context }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function fetchSessionScope(sessionID?: string) {
|
|
194
|
+
if (!sessionID?.trim()) {
|
|
195
|
+
return {
|
|
196
|
+
localSessionId: null,
|
|
197
|
+
isMain: false,
|
|
198
|
+
kind: "other" as const,
|
|
199
|
+
heartbeat: false,
|
|
200
|
+
cronJobId: null,
|
|
201
|
+
cronJobName: null,
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const cacheKey = sessionID.trim()
|
|
206
|
+
const now = Date.now()
|
|
207
|
+
const cached = sessionScopeCache.get(cacheKey)
|
|
208
|
+
if (cached && cached.expiresAtMs > now) {
|
|
209
|
+
return cached.value
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const payload = await requestJson(`/api/mockingbird/runtime/session-scope?sessionId=${encodeURIComponent(cacheKey)}`)
|
|
213
|
+
const kind: SessionScope["kind"] =
|
|
214
|
+
payload.kind === "main" || payload.kind === "cron" || payload.kind === "heartbeat" || payload.kind === "other"
|
|
215
|
+
? payload.kind
|
|
216
|
+
: "other"
|
|
217
|
+
const value = {
|
|
218
|
+
localSessionId: typeof payload.localSessionId === "string" && payload.localSessionId.trim()
|
|
219
|
+
? payload.localSessionId
|
|
220
|
+
: null,
|
|
221
|
+
isMain: payload.isMain === true,
|
|
222
|
+
kind,
|
|
223
|
+
heartbeat: payload.heartbeat === true,
|
|
224
|
+
cronJobId: typeof payload.cronJobId === "string" && payload.cronJobId.trim() ? payload.cronJobId : null,
|
|
225
|
+
cronJobName: typeof payload.cronJobName === "string" && payload.cronJobName.trim() ? payload.cronJobName : null,
|
|
226
|
+
}
|
|
227
|
+
sessionScopeCache.set(cacheKey, {
|
|
228
|
+
value,
|
|
229
|
+
expiresAtMs: now + 5_000,
|
|
230
|
+
})
|
|
231
|
+
return value
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function buildMainThreadNote() {
|
|
235
|
+
return [
|
|
236
|
+
"Thread policy:",
|
|
237
|
+
"- This is the main/root conversation thread.",
|
|
238
|
+
"- Prefer doing work directly in this thread unless delegation materially improves speed or focus.",
|
|
239
|
+
"- Treat this thread as the primary durable context for the user.",
|
|
240
|
+
].join("\n")
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function buildCronThreadNote(sessionScope: SessionScope) {
|
|
244
|
+
const jobLabel = sessionScope.cronJobName
|
|
245
|
+
? `${sessionScope.cronJobName} (${sessionScope.cronJobId ?? "cron"})`
|
|
246
|
+
: sessionScope.cronJobId ?? "this cron job"
|
|
247
|
+
return [
|
|
248
|
+
"Thread policy:",
|
|
249
|
+
`- This thread belongs to cron job ${jobLabel}.`,
|
|
250
|
+
"- Keep work focused on this cron job's ongoing context and prior runs.",
|
|
251
|
+
"- Do not act like this is the main user-facing conversation thread.",
|
|
252
|
+
"- If user attention or a decision is needed, call notify_main_thread with a concise prompt for main.",
|
|
253
|
+
].join("\n")
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function buildHeartbeatThreadNote() {
|
|
257
|
+
return [
|
|
258
|
+
"Thread policy:",
|
|
259
|
+
"- This thread belongs to heartbeat.",
|
|
260
|
+
"- Treat the main/root conversation as the durable source of user context.",
|
|
261
|
+
"- The standard tool surface remains available in this thread.",
|
|
262
|
+
"- Use any available tool when it materially helps the heartbeat do useful work.",
|
|
263
|
+
"- Do not act like this is the main user-facing conversation thread.",
|
|
264
|
+
"- If user attention or a decision is needed, call notify_main_thread with a concise prompt for main.",
|
|
265
|
+
].join("\n")
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const scheduleKindSchema = z.enum(["at", "every", "cron"])
|
|
269
|
+
const runModeSchema = z.enum(["background", "conditional_agent", "agent"])
|
|
270
|
+
const payloadSchema = z.record(z.string(), z.unknown())
|
|
271
|
+
|
|
272
|
+
const jobCreateSchema = z.object({
|
|
273
|
+
id: z.string().min(1).optional(),
|
|
274
|
+
name: z.string().min(1),
|
|
275
|
+
enabled: z.boolean().optional(),
|
|
276
|
+
scheduleKind: scheduleKindSchema,
|
|
277
|
+
scheduleExpr: z.string().min(1).nullable().optional(),
|
|
278
|
+
everyMs: z.number().int().positive().nullable().optional(),
|
|
279
|
+
atIso: z.string().min(1).nullable().optional(),
|
|
280
|
+
timezone: z.string().min(1).nullable().optional(),
|
|
281
|
+
runMode: runModeSchema,
|
|
282
|
+
handlerKey: z.string().min(1).nullable().optional(),
|
|
283
|
+
conditionModulePath: z.string().min(1).nullable().optional(),
|
|
284
|
+
conditionDescription: z.string().min(1).nullable().optional(),
|
|
285
|
+
agentPromptTemplate: z.string().min(1).nullable().optional(),
|
|
286
|
+
agentModelOverride: z.string().min(1).nullable().optional(),
|
|
287
|
+
maxAttempts: z.number().int().positive().optional(),
|
|
288
|
+
retryBackoffMs: z.number().int().positive().optional(),
|
|
289
|
+
payload: payloadSchema.optional(),
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
const jobPatchSchema = z.object({
|
|
293
|
+
name: z.string().min(1).optional(),
|
|
294
|
+
enabled: z.boolean().optional(),
|
|
295
|
+
scheduleKind: scheduleKindSchema.optional(),
|
|
296
|
+
scheduleExpr: z.string().min(1).nullable().optional(),
|
|
297
|
+
everyMs: z.number().int().positive().nullable().optional(),
|
|
298
|
+
atIso: z.string().min(1).nullable().optional(),
|
|
299
|
+
timezone: z.string().min(1).nullable().optional(),
|
|
300
|
+
runMode: runModeSchema.optional(),
|
|
301
|
+
handlerKey: z.string().min(1).nullable().optional(),
|
|
302
|
+
conditionModulePath: z.string().min(1).nullable().optional(),
|
|
303
|
+
conditionDescription: z.string().min(1).nullable().optional(),
|
|
304
|
+
agentPromptTemplate: z.string().min(1).nullable().optional(),
|
|
305
|
+
agentModelOverride: z.string().min(1).nullable().optional(),
|
|
306
|
+
maxAttempts: z.number().int().positive().optional(),
|
|
307
|
+
retryBackoffMs: z.number().int().positive().optional(),
|
|
308
|
+
payload: payloadSchema.optional(),
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
const cronArgsSchema = z.discriminatedUnion("action", [
|
|
312
|
+
z.object({ action: z.literal("list_jobs") }),
|
|
313
|
+
z.object({ action: z.literal("list_handlers") }),
|
|
314
|
+
z.object({ action: z.literal("health") }),
|
|
315
|
+
z.object({ action: z.literal("get_job"), jobId: z.string().min(1) }),
|
|
316
|
+
z.object({ action: z.literal("create_job"), job: jobCreateSchema }),
|
|
317
|
+
z.object({ action: z.literal("upsert_job"), job: jobCreateSchema }),
|
|
318
|
+
z.object({ action: z.literal("update_job"), jobId: z.string().min(1), patch: jobPatchSchema }),
|
|
319
|
+
z.object({ action: z.literal("enable_job"), jobId: z.string().min(1) }),
|
|
320
|
+
z.object({ action: z.literal("disable_job"), jobId: z.string().min(1) }),
|
|
321
|
+
z.object({ action: z.literal("describe_contract") }),
|
|
322
|
+
z.object({ action: z.literal("delete_job"), jobId: z.string().min(1) }),
|
|
323
|
+
z.object({ action: z.literal("run_job_now"), jobId: z.string().min(1) }),
|
|
324
|
+
z.object({ action: z.literal("list_instances"), jobId: z.string().min(1).optional(), limit: z.number().int().positive().optional() }),
|
|
325
|
+
z.object({ action: z.literal("list_steps"), instanceId: z.string().min(1) }),
|
|
326
|
+
])
|
|
327
|
+
|
|
328
|
+
const agentTypeSchema = z.object({
|
|
329
|
+
id: z.string().min(1),
|
|
330
|
+
name: z.string().min(1).optional(),
|
|
331
|
+
description: z.string().min(1).optional(),
|
|
332
|
+
prompt: z.string().min(1).optional(),
|
|
333
|
+
model: z.string().min(1).optional(),
|
|
334
|
+
variant: z.string().min(1).optional(),
|
|
335
|
+
mode: z.enum(["subagent", "primary", "all"]).optional(),
|
|
336
|
+
hidden: z.boolean().optional(),
|
|
337
|
+
disable: z.boolean().optional(),
|
|
338
|
+
temperature: z.number().optional(),
|
|
339
|
+
topP: z.number().optional(),
|
|
340
|
+
steps: z.number().int().positive().optional(),
|
|
341
|
+
permission: z.unknown().optional(),
|
|
342
|
+
options: z.record(z.string(), z.unknown()).optional(),
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
const agentArgsSchema = z.discriminatedUnion("action", [
|
|
346
|
+
z.object({ action: z.literal("list") }),
|
|
347
|
+
z.object({
|
|
348
|
+
action: z.literal("validate_patch"),
|
|
349
|
+
upserts: z.array(agentTypeSchema).default([]),
|
|
350
|
+
deletes: z.array(z.string().min(1)).default([]),
|
|
351
|
+
}),
|
|
352
|
+
z.object({
|
|
353
|
+
action: z.literal("apply_patch"),
|
|
354
|
+
upserts: z.array(agentTypeSchema).default([]),
|
|
355
|
+
deletes: z.array(z.string().min(1)).default([]),
|
|
356
|
+
expectedHash: z.string().min(1),
|
|
357
|
+
}),
|
|
358
|
+
])
|
|
359
|
+
|
|
360
|
+
const configArgsSchema = z.discriminatedUnion("action", [
|
|
361
|
+
z.object({ action: z.literal("get_config") }),
|
|
362
|
+
z.object({
|
|
363
|
+
action: z.literal("patch_config"),
|
|
364
|
+
patch: z.unknown(),
|
|
365
|
+
expectedHash: z.string().min(1),
|
|
366
|
+
runSmokeTest: z.boolean().optional(),
|
|
367
|
+
}),
|
|
368
|
+
z.object({
|
|
369
|
+
action: z.literal("replace_config"),
|
|
370
|
+
config: z.unknown(),
|
|
371
|
+
expectedHash: z.string().min(1),
|
|
372
|
+
runSmokeTest: z.boolean().optional(),
|
|
373
|
+
}),
|
|
374
|
+
])
|
|
375
|
+
|
|
376
|
+
const memorySearchTool = tool({
|
|
377
|
+
description: "Search memory for relevant prior context.",
|
|
378
|
+
args: {
|
|
379
|
+
query: z.string().min(1).describe("Natural language memory query"),
|
|
380
|
+
maxResults: z.number().int().min(1).max(20).optional(),
|
|
381
|
+
minScore: z.number().min(0).max(1).optional(),
|
|
382
|
+
debug: z.boolean().optional().describe("Include retrieval debug details."),
|
|
383
|
+
},
|
|
384
|
+
async execute(args: { query: string; maxResults?: number; minScore?: number; debug?: boolean }) {
|
|
385
|
+
const response = await postJson(
|
|
386
|
+
"/api/mockingbird/memory/retrieve",
|
|
387
|
+
{
|
|
388
|
+
query: args.query,
|
|
389
|
+
maxResults: args.maxResults,
|
|
390
|
+
minScore: args.minScore,
|
|
391
|
+
debug: args.debug,
|
|
392
|
+
},
|
|
393
|
+
["AGENT_MOCKINGBIRD_MEMORY_API_BASE_URL"],
|
|
394
|
+
)
|
|
395
|
+
if (!response.ok) {
|
|
396
|
+
const error = typeof response.payload.error === "string" ? response.payload.error : `Request failed (${response.status})`
|
|
397
|
+
throw new Error(error)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const results = Array.isArray(response.payload.results) ? response.payload.results : []
|
|
401
|
+
const compactResults = results.map((result) => {
|
|
402
|
+
const value = result as JsonObject
|
|
403
|
+
const snippet = typeof value.snippet === "string" ? value.snippet : ""
|
|
404
|
+
return {
|
|
405
|
+
id: value.id,
|
|
406
|
+
score: value.score,
|
|
407
|
+
citation: value.citation,
|
|
408
|
+
path: value.path,
|
|
409
|
+
startLine: value.startLine,
|
|
410
|
+
endLine: value.endLine,
|
|
411
|
+
preview: toPreview(snippet),
|
|
412
|
+
snippet: toPreview(snippet),
|
|
413
|
+
}
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
return JSON.stringify({
|
|
417
|
+
ok: true,
|
|
418
|
+
query: args.query,
|
|
419
|
+
count: compactResults.length,
|
|
420
|
+
results: compactResults,
|
|
421
|
+
debug: args.debug ? response.payload.debug : undefined,
|
|
422
|
+
})
|
|
423
|
+
},
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
const memoryGetTool = tool({
|
|
427
|
+
description: "Read a safe slice of canonical markdown memory files by path and line window.",
|
|
428
|
+
args: {
|
|
429
|
+
path: z.string().min(1).describe("Memory path such as MEMORY.md or memory/2026-02-17.md"),
|
|
430
|
+
from: z.number().int().min(1).optional().describe("Start line number (1-based)"),
|
|
431
|
+
lines: z.number().int().min(1).max(400).optional().describe("Number of lines to return"),
|
|
432
|
+
},
|
|
433
|
+
async execute(args: { path: string; from?: number; lines?: number }) {
|
|
434
|
+
const response = await postJson(
|
|
435
|
+
"/api/mockingbird/memory/read",
|
|
436
|
+
{
|
|
437
|
+
path: args.path,
|
|
438
|
+
from: args.from,
|
|
439
|
+
lines: args.lines,
|
|
440
|
+
},
|
|
441
|
+
["AGENT_MOCKINGBIRD_MEMORY_API_BASE_URL"],
|
|
442
|
+
)
|
|
443
|
+
if (!response.ok) {
|
|
444
|
+
const error = typeof response.payload.error === "string" ? response.payload.error : `Request failed (${response.status})`
|
|
445
|
+
throw new Error(error)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return JSON.stringify({
|
|
449
|
+
ok: true,
|
|
450
|
+
path: response.payload.path,
|
|
451
|
+
text: response.payload.text,
|
|
452
|
+
})
|
|
453
|
+
},
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
const memoryRememberTool = tool({
|
|
457
|
+
description: "Persist a memory note so it can be retrieved later.",
|
|
458
|
+
args: {
|
|
459
|
+
content: z.string().min(1),
|
|
460
|
+
confidence: z.number().min(0).max(1).optional(),
|
|
461
|
+
source: z.enum(["assistant", "user", "system"]).optional(),
|
|
462
|
+
entities: z.array(z.string()).optional(),
|
|
463
|
+
supersedes: z.array(z.string()).optional(),
|
|
464
|
+
topic: z.string().optional(),
|
|
465
|
+
},
|
|
466
|
+
async execute(args: {
|
|
467
|
+
content: string
|
|
468
|
+
confidence?: number
|
|
469
|
+
source?: "assistant" | "user" | "system"
|
|
470
|
+
entities?: string[]
|
|
471
|
+
supersedes?: string[]
|
|
472
|
+
topic?: string
|
|
473
|
+
}, context: ToolContext) {
|
|
474
|
+
const response = await postJson(
|
|
475
|
+
"/api/mockingbird/memory/remember",
|
|
476
|
+
{
|
|
477
|
+
...args,
|
|
478
|
+
source: args.source ?? "assistant",
|
|
479
|
+
sessionId: context.sessionID,
|
|
480
|
+
},
|
|
481
|
+
["AGENT_MOCKINGBIRD_MEMORY_API_BASE_URL"],
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
if (!response.ok && response.status !== 422) {
|
|
485
|
+
const error = typeof response.payload.error === "string" ? response.payload.error : `Request failed (${response.status})`
|
|
486
|
+
throw new Error(error)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return JSON.stringify({
|
|
490
|
+
ok: response.ok,
|
|
491
|
+
status: response.status,
|
|
492
|
+
result: response.payload,
|
|
493
|
+
})
|
|
494
|
+
},
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
const cronManagerTool = tool({
|
|
498
|
+
description: "Manage Agent Mockingbird cron jobs (list/create/update/run/delete/inspect).",
|
|
499
|
+
args: {
|
|
500
|
+
action: z.enum([
|
|
501
|
+
"list_jobs",
|
|
502
|
+
"list_handlers",
|
|
503
|
+
"health",
|
|
504
|
+
"get_job",
|
|
505
|
+
"create_job",
|
|
506
|
+
"upsert_job",
|
|
507
|
+
"update_job",
|
|
508
|
+
"enable_job",
|
|
509
|
+
"disable_job",
|
|
510
|
+
"describe_contract",
|
|
511
|
+
"delete_job",
|
|
512
|
+
"run_job_now",
|
|
513
|
+
"list_instances",
|
|
514
|
+
"list_steps",
|
|
515
|
+
]),
|
|
516
|
+
jobId: z.string().optional(),
|
|
517
|
+
instanceId: z.string().optional(),
|
|
518
|
+
limit: z.number().int().positive().optional(),
|
|
519
|
+
job: z.unknown().optional(),
|
|
520
|
+
patch: z.unknown().optional(),
|
|
521
|
+
},
|
|
522
|
+
async execute(rawArgs) {
|
|
523
|
+
const args = cronArgsSchema.parse(rawArgs)
|
|
524
|
+
const response = await postJson("/api/mockingbird/cron/manage", args, [
|
|
525
|
+
"AGENT_MOCKINGBIRD_CRON_API_BASE_URL",
|
|
526
|
+
"AGENT_MOCKINGBIRD_MEMORY_API_BASE_URL",
|
|
527
|
+
])
|
|
528
|
+
if (!response.ok) {
|
|
529
|
+
const error = typeof response.payload.error === "string" ? response.payload.error : `Request failed (${response.status})`
|
|
530
|
+
throw new Error(error)
|
|
531
|
+
}
|
|
532
|
+
return JSON.stringify({
|
|
533
|
+
ok: true,
|
|
534
|
+
...response.payload,
|
|
535
|
+
})
|
|
536
|
+
},
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
const notifyMainThreadTool = tool({
|
|
540
|
+
description: "Escalate a concise prompt from a cron worker thread into the main/root conversation.",
|
|
541
|
+
args: {
|
|
542
|
+
prompt: z.string().min(1),
|
|
543
|
+
severity: z.enum(["info", "warn", "critical"]).optional(),
|
|
544
|
+
},
|
|
545
|
+
async execute(args: { prompt: string; severity?: "info" | "warn" | "critical" }, context: ToolContext) {
|
|
546
|
+
const response = await postJson("/api/mockingbird/runtime/notify-main-thread", {
|
|
547
|
+
sessionId: context.sessionID,
|
|
548
|
+
prompt: args.prompt,
|
|
549
|
+
severity: args.severity,
|
|
550
|
+
})
|
|
551
|
+
if (!response.ok) {
|
|
552
|
+
const error = typeof response.payload.error === "string" ? response.payload.error : `Request failed (${response.status})`
|
|
553
|
+
throw new Error(error)
|
|
554
|
+
}
|
|
555
|
+
return JSON.stringify({
|
|
556
|
+
ok: true,
|
|
557
|
+
...response.payload,
|
|
558
|
+
})
|
|
559
|
+
},
|
|
560
|
+
})
|
|
561
|
+
|
|
562
|
+
const agentTypeManagerTool = tool({
|
|
563
|
+
description:
|
|
564
|
+
"Manage OpenCode agent definitions through Agent Mockingbird's OpenCode-backed APIs with validation and hash conflict detection.",
|
|
565
|
+
args: {
|
|
566
|
+
action: z.enum(["list", "validate_patch", "apply_patch"]),
|
|
567
|
+
upserts: z.array(z.unknown()).optional(),
|
|
568
|
+
deletes: z.array(z.string().min(1)).optional(),
|
|
569
|
+
expectedHash: z.string().min(1).optional(),
|
|
570
|
+
},
|
|
571
|
+
async execute(rawArgs: {
|
|
572
|
+
action: "list" | "validate_patch" | "apply_patch"
|
|
573
|
+
upserts?: unknown[]
|
|
574
|
+
deletes?: string[]
|
|
575
|
+
expectedHash?: string
|
|
576
|
+
}) {
|
|
577
|
+
const args = agentArgsSchema.parse(rawArgs)
|
|
578
|
+
|
|
579
|
+
if (args.action === "list") {
|
|
580
|
+
const payload = await requestJson("/api/mockingbird/agents")
|
|
581
|
+
return JSON.stringify({ ok: true, ...payload })
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (args.action === "validate_patch") {
|
|
585
|
+
const payload = await requestJson("/api/mockingbird/agents/validate", {
|
|
586
|
+
method: "POST",
|
|
587
|
+
headers: { "Content-Type": "application/json" },
|
|
588
|
+
body: JSON.stringify({
|
|
589
|
+
upserts: args.upserts,
|
|
590
|
+
deletes: args.deletes,
|
|
591
|
+
}),
|
|
592
|
+
})
|
|
593
|
+
return JSON.stringify({ ok: true, ...payload })
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const payload = await requestJson("/api/mockingbird/agents", {
|
|
597
|
+
method: "PATCH",
|
|
598
|
+
headers: { "Content-Type": "application/json" },
|
|
599
|
+
body: JSON.stringify({
|
|
600
|
+
upserts: args.upserts,
|
|
601
|
+
deletes: args.deletes,
|
|
602
|
+
expectedHash: args.expectedHash,
|
|
603
|
+
}),
|
|
604
|
+
})
|
|
605
|
+
return JSON.stringify({ ok: true, ...payload })
|
|
606
|
+
},
|
|
607
|
+
})
|
|
608
|
+
|
|
609
|
+
const configManagerTool = tool({
|
|
610
|
+
description:
|
|
611
|
+
"Read or update Agent Mockingbird managed config through validated APIs with hash conflict detection and optional smoke tests.",
|
|
612
|
+
args: {
|
|
613
|
+
action: z.enum(["get_config", "patch_config", "replace_config"]),
|
|
614
|
+
patch: z.unknown().optional(),
|
|
615
|
+
config: z.unknown().optional(),
|
|
616
|
+
expectedHash: z.string().min(1).optional(),
|
|
617
|
+
runSmokeTest: z.boolean().optional(),
|
|
618
|
+
},
|
|
619
|
+
async execute(rawArgs: {
|
|
620
|
+
action: "get_config" | "patch_config" | "replace_config"
|
|
621
|
+
patch?: unknown
|
|
622
|
+
config?: unknown
|
|
623
|
+
expectedHash?: string
|
|
624
|
+
runSmokeTest?: boolean
|
|
625
|
+
}) {
|
|
626
|
+
const args = configArgsSchema.parse(rawArgs)
|
|
627
|
+
|
|
628
|
+
if (args.action === "get_config") {
|
|
629
|
+
const payload = await requestJson("/api/mockingbird/runtime/config")
|
|
630
|
+
return JSON.stringify({ ok: true, ...payload })
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (args.action === "patch_config") {
|
|
634
|
+
const payload = await requestJson("/api/mockingbird/runtime/config", {
|
|
635
|
+
method: "PATCH",
|
|
636
|
+
headers: { "Content-Type": "application/json" },
|
|
637
|
+
body: JSON.stringify({
|
|
638
|
+
patch: args.patch,
|
|
639
|
+
expectedHash: args.expectedHash,
|
|
640
|
+
}),
|
|
641
|
+
})
|
|
642
|
+
return JSON.stringify({ ok: true, ...payload })
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const payload = await requestJson("/api/mockingbird/runtime/config/replace", {
|
|
646
|
+
method: "POST",
|
|
647
|
+
headers: { "Content-Type": "application/json" },
|
|
648
|
+
body: JSON.stringify({
|
|
649
|
+
config: args.config,
|
|
650
|
+
expectedHash: args.expectedHash,
|
|
651
|
+
}),
|
|
652
|
+
})
|
|
653
|
+
return JSON.stringify({ ok: true, ...payload })
|
|
654
|
+
},
|
|
655
|
+
})
|
|
656
|
+
|
|
657
|
+
const AgentMockingbirdPlugin: Plugin = async () => {
|
|
658
|
+
return {
|
|
659
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
660
|
+
const system = await fetchSystemPrompt()
|
|
661
|
+
if (!system.trim()) return
|
|
662
|
+
output.system.push(system)
|
|
663
|
+
const sessionScope = await fetchSessionScope(_input.sessionID)
|
|
664
|
+
if (sessionScope.isMain) {
|
|
665
|
+
output.system.push(buildMainThreadNote())
|
|
666
|
+
} else if (sessionScope.kind === "cron") {
|
|
667
|
+
output.system.push(buildCronThreadNote(sessionScope))
|
|
668
|
+
} else if (sessionScope.kind === "heartbeat") {
|
|
669
|
+
output.system.push(buildHeartbeatThreadNote())
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
"experimental.session.compacting": async (_input, output) => {
|
|
673
|
+
const compaction = await fetchCompactionContext(_input.sessionID)
|
|
674
|
+
if (compaction.prompt) {
|
|
675
|
+
output.prompt = compaction.prompt
|
|
676
|
+
return
|
|
677
|
+
}
|
|
678
|
+
if (compaction.context.length === 0) return
|
|
679
|
+
output.context.push(...compaction.context)
|
|
680
|
+
},
|
|
681
|
+
"tool.definition": async (input, output) => {
|
|
682
|
+
rewriteToolDefinition(input.toolID, output)
|
|
683
|
+
},
|
|
684
|
+
"shell.env": async (_input, output) => {
|
|
685
|
+
const defaultBaseUrl = resolveApiBaseUrl(
|
|
686
|
+
"AGENT_MOCKINGBIRD_CONFIG_API_BASE_URL",
|
|
687
|
+
"AGENT_MOCKINGBIRD_MEMORY_API_BASE_URL",
|
|
688
|
+
"AGENT_MOCKINGBIRD_CRON_API_BASE_URL",
|
|
689
|
+
)
|
|
690
|
+
output.env.AGENT_MOCKINGBIRD_CONFIG_API_BASE_URL ??=
|
|
691
|
+
process.env.AGENT_MOCKINGBIRD_CONFIG_API_BASE_URL?.trim() || defaultBaseUrl
|
|
692
|
+
output.env.AGENT_MOCKINGBIRD_MEMORY_API_BASE_URL ??=
|
|
693
|
+
process.env.AGENT_MOCKINGBIRD_MEMORY_API_BASE_URL?.trim() || defaultBaseUrl
|
|
694
|
+
output.env.AGENT_MOCKINGBIRD_CRON_API_BASE_URL ??=
|
|
695
|
+
process.env.AGENT_MOCKINGBIRD_CRON_API_BASE_URL?.trim() ||
|
|
696
|
+
process.env.AGENT_MOCKINGBIRD_MEMORY_API_BASE_URL?.trim() ||
|
|
697
|
+
defaultBaseUrl
|
|
698
|
+
if (process.env.AGENT_MOCKINGBIRD_PORT?.trim()) {
|
|
699
|
+
output.env.AGENT_MOCKINGBIRD_PORT ??= process.env.AGENT_MOCKINGBIRD_PORT.trim()
|
|
700
|
+
}
|
|
701
|
+
},
|
|
702
|
+
tool: {
|
|
703
|
+
memory_search: memorySearchTool,
|
|
704
|
+
memory_get: memoryGetTool,
|
|
705
|
+
memory_remember: memoryRememberTool,
|
|
706
|
+
cron_manager: cronManagerTool,
|
|
707
|
+
notify_main_thread: notifyMainThreadTool,
|
|
708
|
+
agent_type_manager: agentTypeManagerTool,
|
|
709
|
+
config_manager: configManagerTool,
|
|
710
|
+
},
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
export { AgentMockingbirdPlugin }
|
|
715
|
+
export default AgentMockingbirdPlugin
|