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.
Files changed (227) hide show
  1. package/.agents/skills/btca-cli/SKILL.md +64 -0
  2. package/.agents/skills/btca-cli/agents/openai.yaml +3 -0
  3. package/.agents/skills/frontend-design/SKILL.md +42 -0
  4. package/.agents/skills/frontend-design/agents/openai.yaml +3 -0
  5. package/.env.example +36 -0
  6. package/.githooks/pre-commit +33 -0
  7. package/.github/workflows/ci.yml +309 -0
  8. package/.opencode/bun.lock +18 -0
  9. package/.opencode/package.json +5 -0
  10. package/.opencode/tools/agent_type_manager.ts +100 -0
  11. package/.opencode/tools/config_manager.ts +87 -0
  12. package/.opencode/tools/cron_manager.ts +145 -0
  13. package/.opencode/tools/memory_get.ts +43 -0
  14. package/.opencode/tools/memory_remember.ts +53 -0
  15. package/.opencode/tools/memory_search.ts +48 -0
  16. package/AGENTS.md +126 -0
  17. package/MEMORY.md +2 -0
  18. package/README.md +451 -0
  19. package/THIRD_PARTY_NOTICES.md +11 -0
  20. package/agent-mockingbird.config.example.json +135 -0
  21. package/apps/server/package.json +32 -0
  22. package/apps/server/src/backend/agents/bootstrapContext.ts +362 -0
  23. package/apps/server/src/backend/agents/openclawImport.test.ts +133 -0
  24. package/apps/server/src/backend/agents/openclawImport.ts +797 -0
  25. package/apps/server/src/backend/agents/opencodeConfig.ts +428 -0
  26. package/apps/server/src/backend/agents/service.ts +10 -0
  27. package/apps/server/src/backend/config/example-config.test.ts +20 -0
  28. package/apps/server/src/backend/config/orchestration.ts +243 -0
  29. package/apps/server/src/backend/config/policy.ts +158 -0
  30. package/apps/server/src/backend/config/schema.test.ts +15 -0
  31. package/apps/server/src/backend/config/schema.ts +391 -0
  32. package/apps/server/src/backend/config/semantic.test.ts +34 -0
  33. package/apps/server/src/backend/config/semantic.ts +149 -0
  34. package/apps/server/src/backend/config/service.test.ts +75 -0
  35. package/apps/server/src/backend/config/service.ts +207 -0
  36. package/apps/server/src/backend/config/smoke.ts +77 -0
  37. package/apps/server/src/backend/config/store.test.ts +123 -0
  38. package/apps/server/src/backend/config/store.ts +581 -0
  39. package/apps/server/src/backend/config/testFixtures.ts +5 -0
  40. package/apps/server/src/backend/config/types.ts +56 -0
  41. package/apps/server/src/backend/contracts/events.ts +320 -0
  42. package/apps/server/src/backend/contracts/runtime.ts +111 -0
  43. package/apps/server/src/backend/cron/executor.ts +435 -0
  44. package/apps/server/src/backend/cron/repository.ts +170 -0
  45. package/apps/server/src/backend/cron/service.ts +660 -0
  46. package/apps/server/src/backend/cron/storage.ts +92 -0
  47. package/apps/server/src/backend/cron/types.ts +138 -0
  48. package/apps/server/src/backend/cron/utils.ts +351 -0
  49. package/apps/server/src/backend/db/client.ts +20 -0
  50. package/apps/server/src/backend/db/migrate.ts +40 -0
  51. package/apps/server/src/backend/db/repository.ts +1762 -0
  52. package/apps/server/src/backend/db/schema.ts +113 -0
  53. package/apps/server/src/backend/db/usageDashboard.test.ts +102 -0
  54. package/apps/server/src/backend/db/wipe.ts +13 -0
  55. package/apps/server/src/backend/defaults.ts +32 -0
  56. package/apps/server/src/backend/env.ts +48 -0
  57. package/apps/server/src/backend/heartbeat/activeHours.ts +45 -0
  58. package/apps/server/src/backend/heartbeat/defaultJob.ts +88 -0
  59. package/apps/server/src/backend/heartbeat/heartbeat.test.ts +110 -0
  60. package/apps/server/src/backend/heartbeat/runtimeService.ts +190 -0
  61. package/apps/server/src/backend/heartbeat/service.ts +176 -0
  62. package/apps/server/src/backend/heartbeat/state.test.ts +63 -0
  63. package/apps/server/src/backend/heartbeat/state.ts +167 -0
  64. package/apps/server/src/backend/heartbeat/types.ts +54 -0
  65. package/apps/server/src/backend/http/boundedQueue.test.ts +49 -0
  66. package/apps/server/src/backend/http/boundedQueue.ts +92 -0
  67. package/apps/server/src/backend/http/parsers.ts +40 -0
  68. package/apps/server/src/backend/http/router.ts +61 -0
  69. package/apps/server/src/backend/http/routes/agentRoutes.ts +67 -0
  70. package/apps/server/src/backend/http/routes/backgroundRoutes.ts +203 -0
  71. package/apps/server/src/backend/http/routes/chatRoutes.ts +107 -0
  72. package/apps/server/src/backend/http/routes/configRoutes.ts +602 -0
  73. package/apps/server/src/backend/http/routes/cronRoutes.ts +221 -0
  74. package/apps/server/src/backend/http/routes/dashboardRoutes.ts +308 -0
  75. package/apps/server/src/backend/http/routes/eventRoutes.ts +7 -0
  76. package/apps/server/src/backend/http/routes/heartbeatRoutes.test.ts +41 -0
  77. package/apps/server/src/backend/http/routes/heartbeatRoutes.ts +28 -0
  78. package/apps/server/src/backend/http/routes/index.ts +101 -0
  79. package/apps/server/src/backend/http/routes/mcpRoutes.ts +213 -0
  80. package/apps/server/src/backend/http/routes/memoryRoutes.ts +154 -0
  81. package/apps/server/src/backend/http/routes/runRoutes.ts +310 -0
  82. package/apps/server/src/backend/http/routes/runtimeRoutes.ts +197 -0
  83. package/apps/server/src/backend/http/routes/skillRoutes.ts +112 -0
  84. package/apps/server/src/backend/http/routes/uiRoutes.test.ts +161 -0
  85. package/apps/server/src/backend/http/routes/uiRoutes.ts +177 -0
  86. package/apps/server/src/backend/http/routes/usageRoutes.test.ts +104 -0
  87. package/apps/server/src/backend/http/routes/usageRoutes.ts +767 -0
  88. package/apps/server/src/backend/http/schemas.ts +64 -0
  89. package/apps/server/src/backend/http/sse.ts +144 -0
  90. package/apps/server/src/backend/integration/backend-core.test.ts +2316 -0
  91. package/apps/server/src/backend/logging/logger.ts +64 -0
  92. package/apps/server/src/backend/mcp/service.ts +326 -0
  93. package/apps/server/src/backend/memory/cli.ts +170 -0
  94. package/apps/server/src/backend/memory/conceptExpansion.test.ts +28 -0
  95. package/apps/server/src/backend/memory/conceptExpansion.ts +80 -0
  96. package/apps/server/src/backend/memory/qmdPort.test.ts +54 -0
  97. package/apps/server/src/backend/memory/qmdPort.ts +61 -0
  98. package/apps/server/src/backend/memory/records.test.ts +66 -0
  99. package/apps/server/src/backend/memory/records.ts +229 -0
  100. package/apps/server/src/backend/memory/service.ts +2012 -0
  101. package/apps/server/src/backend/memory/sqliteVec.ts +58 -0
  102. package/apps/server/src/backend/memory/types.ts +104 -0
  103. package/apps/server/src/backend/opencode/agentMockingbirdPlugin.test.ts +396 -0
  104. package/apps/server/src/backend/opencode/client.ts +98 -0
  105. package/apps/server/src/backend/opencode/models.ts +41 -0
  106. package/apps/server/src/backend/opencode/systemPrompt.test.ts +146 -0
  107. package/apps/server/src/backend/opencode/systemPrompt.ts +284 -0
  108. package/apps/server/src/backend/paths.ts +57 -0
  109. package/apps/server/src/backend/prompts/service.ts +100 -0
  110. package/apps/server/src/backend/queue/queue.test.ts +189 -0
  111. package/apps/server/src/backend/queue/service.ts +177 -0
  112. package/apps/server/src/backend/queue/types.ts +39 -0
  113. package/apps/server/src/backend/run/service.ts +576 -0
  114. package/apps/server/src/backend/run/storage.ts +47 -0
  115. package/apps/server/src/backend/run/types.ts +44 -0
  116. package/apps/server/src/backend/runtime/errors.ts +61 -0
  117. package/apps/server/src/backend/runtime/index.ts +72 -0
  118. package/apps/server/src/backend/runtime/memoryPromptDedup.test.ts +153 -0
  119. package/apps/server/src/backend/runtime/memoryPromptDedup.ts +76 -0
  120. package/apps/server/src/backend/runtime/opencodeRuntime/backgroundMethods.ts +765 -0
  121. package/apps/server/src/backend/runtime/opencodeRuntime/coreMethods.ts +705 -0
  122. package/apps/server/src/backend/runtime/opencodeRuntime/eventMethods.ts +503 -0
  123. package/apps/server/src/backend/runtime/opencodeRuntime/memoryMethods.ts +462 -0
  124. package/apps/server/src/backend/runtime/opencodeRuntime/promptMethods.ts +1167 -0
  125. package/apps/server/src/backend/runtime/opencodeRuntime/shared.ts +254 -0
  126. package/apps/server/src/backend/runtime/opencodeRuntime.test.ts +2899 -0
  127. package/apps/server/src/backend/runtime/opencodeRuntime.ts +135 -0
  128. package/apps/server/src/backend/runtime/sessionScope.ts +45 -0
  129. package/apps/server/src/backend/skills/service.ts +442 -0
  130. package/apps/server/src/backend/workspace/resolve.ts +27 -0
  131. package/apps/server/src/cli/agent-mockingbird.mjs +2522 -0
  132. package/apps/server/src/cli/agent-mockingbird.test.ts +68 -0
  133. package/apps/server/src/cli/runtime-assets.mjs +269 -0
  134. package/apps/server/src/cli/runtime-assets.test.ts +52 -0
  135. package/apps/server/src/cli/runtime-layout.mjs +75 -0
  136. package/apps/server/src/cli/standaloneBuild.test.ts +19 -0
  137. package/apps/server/src/cli/standaloneBuild.ts +19 -0
  138. package/apps/server/src/cli/standaloneCronBinary.test.ts +187 -0
  139. package/apps/server/src/index.ts +178 -0
  140. package/apps/server/tsconfig.json +12 -0
  141. package/backlog.md +5 -0
  142. package/bin/agent-mockingbird +2522 -0
  143. package/bin/runtime-layout.mjs +75 -0
  144. package/build-bin.ts +34 -0
  145. package/build-cli.mjs +37 -0
  146. package/build.ts +40 -0
  147. package/bun-env.d.ts +11 -0
  148. package/bun.lock +888 -0
  149. package/bunfig.toml +2 -0
  150. package/components.json +21 -0
  151. package/config.json +130 -0
  152. package/deploy/RELEASE_INSTALL.md +112 -0
  153. package/deploy/docker-compose.yml +42 -0
  154. package/deploy/systemd/README.md +46 -0
  155. package/deploy/systemd/agent-mockingbird.service +28 -0
  156. package/deploy/systemd/opencode.service +25 -0
  157. package/docs/legacy-config-ui-reference.md +51 -0
  158. package/docs/memory-e2e-trace-2026-03-04.md +63 -0
  159. package/docs/memory-ops.md +96 -0
  160. package/docs/memory-runtime-contract.md +42 -0
  161. package/docs/memory-tuning-remote-2026-03-04.md +59 -0
  162. package/docs/opencode-rebase-workflow-plan.md +614 -0
  163. package/docs/opencode-startup-sync-plan.md +94 -0
  164. package/docs/vendor-opencode.md +41 -0
  165. package/drizzle/0000_famous_turbo.sql +49 -0
  166. package/drizzle/0001_cron_memory_aux.sql +160 -0
  167. package/drizzle/0002_runtime_session_bindings.sql +28 -0
  168. package/drizzle/0003_background_runs.sql +27 -0
  169. package/drizzle/0004_memory_open_write.sql +63 -0
  170. package/drizzle/0005_signal_channel.sql +47 -0
  171. package/drizzle/0006_usage_event_dimensions.sql +7 -0
  172. package/drizzle/meta/0000_snapshot.json +341 -0
  173. package/drizzle/meta/_journal.json +55 -0
  174. package/drizzle.config.ts +14 -0
  175. package/eslint.config.mjs +77 -0
  176. package/knip.json +18 -0
  177. package/memory/2026-03-04.md +4 -0
  178. package/opencode.lock.json +16 -0
  179. package/package.json +67 -0
  180. package/packages/agent-mockingbird-installer/README.md +31 -0
  181. package/packages/agent-mockingbird-installer/bin/agent-mockingbird-installer.mjs +44 -0
  182. package/packages/agent-mockingbird-installer/opencode.lock.json +16 -0
  183. package/packages/agent-mockingbird-installer/package.json +23 -0
  184. package/packages/contracts/package.json +19 -0
  185. package/packages/contracts/src/agentTypes.ts +122 -0
  186. package/packages/contracts/src/cron.ts +146 -0
  187. package/packages/contracts/src/dashboard.ts +378 -0
  188. package/packages/contracts/src/index.ts +3 -0
  189. package/packages/contracts/tsconfig.json +4 -0
  190. package/patches/opencode/0001-Wafflebot-OpenCode-baseline.patch +2341 -0
  191. package/patches/opencode/0002-Fix-OpenCode-web-entry-and-settings-icons.patch +104 -0
  192. package/patches/opencode/0003-fix-app-remove-duplicate-sidebar-mount.patch +32 -0
  193. package/patches/opencode/0004-Add-heartbeat-settings-and-usage-nav.patch +506 -0
  194. package/patches/opencode/0005-Use-chart-icon-for-usage-nav.patch +38 -0
  195. package/patches/opencode/0006-Modernize-cron-settings.patch +399 -0
  196. package/patches/opencode/0007-Rename-waffle-namespaces-to-mockingbird.patch +1110 -0
  197. package/patches/opencode/0008-Remove-cron-contract-section.patch +178 -0
  198. package/patches/opencode/0009-Rework-cron-tab-as-operations-console.patch +414 -0
  199. package/patches/opencode/0010-Refine-heartbeat-settings-controls.patch +208 -0
  200. package/runtime-assets/opencode-config/opencode.jsonc +25 -0
  201. package/runtime-assets/opencode-config/package.json +5 -0
  202. package/runtime-assets/opencode-config/plugins/agent-mockingbird.ts +715 -0
  203. package/runtime-assets/workspace/.agents/skills/config-auditor/SKILL.md +25 -0
  204. package/runtime-assets/workspace/.agents/skills/config-editor/SKILL.md +24 -0
  205. package/runtime-assets/workspace/.agents/skills/cron-manager/SKILL.md +57 -0
  206. package/runtime-assets/workspace/.agents/skills/memory-ops/SKILL.md +120 -0
  207. package/runtime-assets/workspace/.agents/skills/runtime-diagnose/SKILL.md +25 -0
  208. package/runtime-assets/workspace/AGENTS.md +56 -0
  209. package/runtime-assets/workspace/MEMORY.md +4 -0
  210. package/scripts/build-release-bundle.sh +66 -0
  211. package/scripts/check-ship.ts +383 -0
  212. package/scripts/dev-opencode.sh +17 -0
  213. package/scripts/dev-stack-opencode.sh +15 -0
  214. package/scripts/dev-stack.sh +61 -0
  215. package/scripts/install-systemd.sh +87 -0
  216. package/scripts/memory-e2e.sh +76 -0
  217. package/scripts/memory-trace-e2e.sh +141 -0
  218. package/scripts/migrate-opencode-env.ts +108 -0
  219. package/scripts/onboard/bootstrap.sh +32 -0
  220. package/scripts/opencode-swap.ts +78 -0
  221. package/scripts/opencode-sync.ts +715 -0
  222. package/scripts/runtime-assets-sync.mjs +83 -0
  223. package/scripts/setup-git-hooks.ts +39 -0
  224. package/tsconfig.json +45 -0
  225. package/tui.json +98 -0
  226. package/turbo.json +36 -0
  227. package/vendor/OPENCODE_VENDOR.md +13 -0
@@ -0,0 +1,383 @@
1
+ #!/usr/bin/env bun
2
+ import { existsSync, readdirSync, statSync } from "node:fs";
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+ import { spawnSync } from "node:child_process";
6
+
7
+ type CommandResult = {
8
+ status: number;
9
+ stdout: string;
10
+ stderr: string;
11
+ combined: string;
12
+ };
13
+
14
+ type SyncStatus = {
15
+ cleanroom: {
16
+ exists: boolean;
17
+ pristine: boolean | null;
18
+ matchesLock: boolean | null;
19
+ path: string;
20
+ };
21
+ vendor: {
22
+ exists: boolean;
23
+ state: "missing" | "clean" | "dirty" | "conflicted" | "invalid";
24
+ path: string;
25
+ };
26
+ patches: {
27
+ matchesBranch: boolean | null;
28
+ };
29
+ };
30
+
31
+ type Diagnostic = {
32
+ key: string;
33
+ line: string;
34
+ };
35
+
36
+ const repoRoot = path.resolve(import.meta.dir, "..");
37
+ const cleanroomRoot = path.join(repoRoot, "cleanroom", "opencode");
38
+ const vendorRoot = path.join(repoRoot, "vendor", "opencode");
39
+ const trackedArtifactPaths = ["bin/agent-mockingbird"];
40
+
41
+ main();
42
+
43
+ function main() {
44
+ ensureOpencodeWorktree();
45
+ const initialStatus = readSyncStatus();
46
+ if (!initialStatus) {
47
+ fail("Unable to read OpenCode sync status after worktree setup.");
48
+ }
49
+ assertShipState(initialStatus);
50
+
51
+ runStep("Build CLI", ["bun", "run", "build:cli"]);
52
+ assertTrackedArtifactsSynced(["bin/agent-mockingbird"]);
53
+
54
+ runStep("Lint", ["bun", "run", "lint"]);
55
+ runStep("Typecheck", ["bun", "run", "typecheck"]);
56
+
57
+ runStep("OpenCode Patch Check", ["bun", "run", "opencode:sync", "--check"]);
58
+ runFilteredOpencodeTypecheck();
59
+
60
+ runStep("Build", ["bun", "run", "build"]);
61
+ assertDistAppBuilt();
62
+ assertTrackedArtifactsSynced(trackedArtifactPaths);
63
+
64
+ runStep("Build Standalone Runtime", ["bun", "run", "build:bin"]);
65
+ assertTrackedArtifactsSynced(["bin/agent-mockingbird"]);
66
+
67
+ console.log("\nShip check passed.");
68
+ }
69
+
70
+ function ensureOpencodeWorktree() {
71
+ const status = readSyncStatus({ allowFailure: true });
72
+ if (status && status.vendor.exists) {
73
+ return;
74
+ }
75
+
76
+ console.log("\n== Materialize OpenCode Worktree ==");
77
+ runCommand(["bun", "run", "opencode:sync", "--rebuild-only"]);
78
+ }
79
+
80
+ function assertShipState(status: SyncStatus) {
81
+ const problems: string[] = [];
82
+
83
+ if (!status.cleanroom.exists) {
84
+ problems.push("cleanroom/opencode is missing");
85
+ }
86
+ if (status.cleanroom.pristine !== true) {
87
+ problems.push("cleanroom/opencode is not pristine");
88
+ }
89
+ if (status.cleanroom.matchesLock !== true) {
90
+ problems.push("cleanroom/opencode does not match opencode.lock.json");
91
+ }
92
+ if (!status.vendor.exists) {
93
+ problems.push("vendor/opencode is missing");
94
+ }
95
+ if (status.vendor.state !== "clean") {
96
+ problems.push(`vendor/opencode is ${status.vendor.state}`);
97
+ }
98
+ if (status.patches.matchesBranch !== true) {
99
+ problems.push("patches/opencode does not match the vendor patch branch");
100
+ }
101
+
102
+ if (problems.length === 0) {
103
+ return;
104
+ }
105
+
106
+ const detail = problems.map((item) => `- ${item}`).join("\n");
107
+ fail(`OpenCode ship state is not clean:\n${detail}\n\nCommit/export vendor changes or rebuild the worktree before shipping.`);
108
+ }
109
+
110
+ function runFilteredOpencodeTypecheck() {
111
+ console.log("\n== OpenCode Dependency Install ==");
112
+ runCommand(["bun", "install", "--cwd", cleanroomRoot, "--frozen-lockfile"], {
113
+ quietLabel: "cleanroom/opencode",
114
+ env: bunInstallEnv(),
115
+ });
116
+ runCommand(["bun", "install", "--cwd", vendorRoot, "--frozen-lockfile"], {
117
+ quietLabel: "vendor/opencode",
118
+ env: bunInstallEnv(),
119
+ });
120
+
121
+ console.log("\n== OpenCode Full Typecheck ==");
122
+ const cleanroomResult = runCommand(
123
+ ["bun", "run", "--cwd", cleanroomRoot, "typecheck"],
124
+ { allowFailure: true, quietLabel: "cleanroom/opencode" },
125
+ );
126
+ const vendorResult = runCommand(
127
+ ["bun", "run", "--cwd", vendorRoot, "typecheck"],
128
+ { allowFailure: true, quietLabel: "vendor/opencode" },
129
+ );
130
+
131
+ if (cleanroomResult.status === 0 && vendorResult.status === 0) {
132
+ console.log("OpenCode cleanroom and vendor workspaces both typecheck cleanly.");
133
+ return;
134
+ }
135
+
136
+ if (cleanroomResult.status === 0 && vendorResult.status !== 0) {
137
+ fail(
138
+ "Patched OpenCode workspace typecheck failed while cleanroom passed.\n\nVendor output:\n" +
139
+ indentBlock(vendorResult.combined || "<no output>"),
140
+ );
141
+ }
142
+
143
+ const cleanroomDiagnostics = collectDiagnostics(cleanroomResult.combined, cleanroomRoot);
144
+ const vendorDiagnostics = collectDiagnostics(vendorResult.combined, vendorRoot);
145
+ const cleanroomKeys = new Set(cleanroomDiagnostics.map((item) => item.key));
146
+ const vendorOnlyDiagnostics = vendorDiagnostics.filter((item) => !cleanroomKeys.has(item.key));
147
+
148
+ if (vendorOnlyDiagnostics.length > 0) {
149
+ const detail = vendorOnlyDiagnostics.map((item) => `- ${item.line}`).join("\n");
150
+ fail(`Patched OpenCode introduced new typecheck diagnostics:\n${detail}`);
151
+ }
152
+
153
+ if (vendorDiagnostics.length === 0 && cleanroomDiagnostics.length === 0) {
154
+ const cleanroomComparable = normalizeComparableOutput(cleanroomResult.combined, cleanroomRoot);
155
+ const vendorComparable = normalizeComparableOutput(vendorResult.combined, vendorRoot);
156
+ if (cleanroomComparable !== vendorComparable) {
157
+ fail(
158
+ "OpenCode workspace typecheck failed in both cleanroom and vendor, but the failures do not normalize to the same output.\n\nVendor output:\n" +
159
+ indentBlock(vendorResult.combined || "<no output>"),
160
+ );
161
+ }
162
+ }
163
+
164
+ if (vendorResult.status !== 0) {
165
+ const baselineCount = vendorDiagnostics.length || countComparableLines(normalizeComparableOutput(vendorResult.combined, vendorRoot));
166
+ console.log(`Ignoring ${baselineCount} cleanroom-matching OpenCode typecheck issue(s).`);
167
+ if (vendorDiagnostics.length > 0) {
168
+ console.log("Ignored baseline diagnostics:");
169
+ for (const diagnostic of vendorDiagnostics) {
170
+ console.log(`- ${diagnostic.line}`);
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+ function collectDiagnostics(output: string, workspaceRoot: string) {
177
+ const diagnostics = new Map<string, Diagnostic>();
178
+ const lines = stripAnsi(output).split(/\r?\n/);
179
+
180
+ for (const rawLine of lines) {
181
+ const line = rawLine.trimEnd();
182
+ const match = line.match(
183
+ /^(?<task>.+?:typecheck:\s+)?(?<file>.+?)\((?<line>\d+),(?<column>\d+)\): error TS(?<code>\d+): (?<message>.+)$/,
184
+ );
185
+ if (!match?.groups) {
186
+ continue;
187
+ }
188
+
189
+ const file = normalizePath(match.groups.file, workspaceRoot);
190
+ const row = match.groups.line;
191
+ const column = match.groups.column;
192
+ const code = `TS${match.groups.code}`;
193
+ const message = match.groups.message.trim();
194
+ const key = [file, row, column, code, message].join("|");
195
+ diagnostics.set(key, { key, line: `${file}(${row},${column}): error ${code}: ${message}` });
196
+ }
197
+
198
+ return [...diagnostics.values()];
199
+ }
200
+
201
+ function normalizeComparableOutput(output: string, workspaceRoot: string) {
202
+ const lines = stripAnsi(output)
203
+ .split(/\r?\n/)
204
+ .map((line) => line.trimEnd())
205
+ .filter(Boolean)
206
+ .filter((line) => !isNoiseLine(line))
207
+ .map((line) => normalizeRoot(line, workspaceRoot));
208
+ return lines.join("\n");
209
+ }
210
+
211
+ function countComparableLines(output: string) {
212
+ if (!output) {
213
+ return 0;
214
+ }
215
+ return output.split("\n").filter(Boolean).length;
216
+ }
217
+
218
+ function isNoiseLine(line: string) {
219
+ return (
220
+ line === "$ bun turbo typecheck" ||
221
+ line.startsWith("• turbo ") ||
222
+ line.startsWith("• Packages in scope:") ||
223
+ line.startsWith("• Running typecheck in ") ||
224
+ line.startsWith("• Remote caching ") ||
225
+ line.startsWith("Tasks:") ||
226
+ line.startsWith("Cached:") ||
227
+ line.startsWith("Time:")
228
+ );
229
+ }
230
+
231
+ function normalizePath(filePath: string, workspaceRoot: string) {
232
+ const normalized = filePath.replaceAll("\\", "/");
233
+ if (path.isAbsolute(normalized)) {
234
+ return normalizeRoot(normalized, workspaceRoot);
235
+ }
236
+ return normalized.replace(/^\.\//, "");
237
+ }
238
+
239
+ function normalizeRoot(value: string, workspaceRoot: string) {
240
+ const normalizedRoot = workspaceRoot.replaceAll("\\", "/");
241
+ return value.replaceAll(normalizedRoot, "<workspace>").replaceAll(repoRoot.replaceAll("\\", "/"), "<repo>");
242
+ }
243
+
244
+ function assertDistAppBuilt() {
245
+ const indexPath = path.join(repoRoot, "dist", "app", "index.html");
246
+ const assetsPath = path.join(repoRoot, "dist", "app", "assets");
247
+
248
+ if (!existsSync(indexPath)) {
249
+ fail("Missing dist/app/index.html after build.");
250
+ }
251
+ if (!existsSync(assetsPath) || !directoryHasFiles(assetsPath)) {
252
+ fail("Missing built OpenCode app assets in dist/app/assets after build.");
253
+ }
254
+ }
255
+
256
+ function directoryHasFiles(targetPath: string): boolean {
257
+ for (const entry of readdirSync(targetPath)) {
258
+ const entryPath = path.join(targetPath, entry);
259
+ const stats = statSync(entryPath);
260
+ if (stats.isFile()) {
261
+ return true;
262
+ }
263
+ if (stats.isDirectory() && directoryHasFiles(entryPath)) {
264
+ return true;
265
+ }
266
+ }
267
+ return false;
268
+ }
269
+
270
+ function assertTrackedArtifactsSynced(pathsToCheck: string[]) {
271
+ const result = runCommand(["git", "diff", "--name-only", "--", ...pathsToCheck], { allowFailure: true, printCommand: false });
272
+ const changed = result.stdout
273
+ .split(/\r?\n/)
274
+ .map((line) => line.trim())
275
+ .filter(Boolean);
276
+ if (changed.length === 0) {
277
+ return;
278
+ }
279
+
280
+ const detail = changed.map((item) => `- ${item}`).join("\n");
281
+ fail(`Generated artifacts are out of sync:\n${detail}\n\nRebuild and commit the generated outputs before shipping.`);
282
+ }
283
+
284
+ function readSyncStatus(options?: { allowFailure?: boolean }): SyncStatus | null {
285
+ const result = runCommand(
286
+ ["bun", "run", "opencode:sync", "--status", "--json"],
287
+ { allowFailure: options?.allowFailure ?? false, printCommand: false, suppressOutput: true },
288
+ );
289
+ if (result.status !== 0) {
290
+ return null;
291
+ }
292
+
293
+ try {
294
+ return JSON.parse(result.stdout) as SyncStatus;
295
+ } catch (error) {
296
+ const message = error instanceof Error ? error.message : String(error);
297
+ fail(`Failed to parse opencode sync status JSON: ${message}`);
298
+ }
299
+ }
300
+
301
+ function runStep(label: string, command: string[]) {
302
+ console.log(`\n== ${label} ==`);
303
+ runCommand(command);
304
+ }
305
+
306
+ function runCommand(
307
+ command: string[],
308
+ options: {
309
+ allowFailure?: boolean;
310
+ cwd?: string;
311
+ printCommand?: boolean;
312
+ quietLabel?: string;
313
+ suppressOutput?: boolean;
314
+ env?: NodeJS.ProcessEnv;
315
+ } = {},
316
+ ): CommandResult {
317
+ const cwd = options.cwd ?? repoRoot;
318
+ if (options.printCommand !== false) {
319
+ console.log(`$ ${command.join(" ")}`);
320
+ }
321
+
322
+ const result = spawnSync(command[0], command.slice(1), {
323
+ cwd,
324
+ encoding: "utf8",
325
+ env: options.env ?? process.env,
326
+ });
327
+
328
+ const stdout = result.stdout || "";
329
+ const stderr = result.stderr || "";
330
+ const combined = [stdout, stderr].filter(Boolean).join(stdout && stderr ? "\n" : "");
331
+
332
+ if (options.suppressOutput) {
333
+ // Intentionally silent.
334
+ } else if (options.quietLabel) {
335
+ const summary = result.status === 0 ? "passed" : `failed (${result.status ?? "unknown"})`;
336
+ console.log(`${options.quietLabel}: ${summary}`);
337
+ } else {
338
+ if (stdout) {
339
+ process.stdout.write(stdout);
340
+ }
341
+ if (stderr) {
342
+ process.stderr.write(stderr);
343
+ }
344
+ }
345
+
346
+ if ((result.status ?? 0) !== 0 && !options.allowFailure) {
347
+ fail(
348
+ `Command failed: ${command.join(" ")}\n\n${combined ? indentBlock(combined) : " <no output>"}`,
349
+ result.status ?? 1,
350
+ );
351
+ }
352
+
353
+ return {
354
+ status: result.status ?? 0,
355
+ stdout,
356
+ stderr,
357
+ combined,
358
+ };
359
+ }
360
+
361
+ function indentBlock(value: string) {
362
+ return value
363
+ .split(/\r?\n/)
364
+ .map((line) => ` ${line}`)
365
+ .join("\n");
366
+ }
367
+
368
+ function stripAnsi(value: string) {
369
+ return value.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g, "");
370
+ }
371
+
372
+ function bunInstallEnv() {
373
+ return {
374
+ ...process.env,
375
+ GITHUB_SERVER_URL: "https://github.com",
376
+ GITHUB_API_URL: "https://api.github.com",
377
+ };
378
+ }
379
+
380
+ function fail(message: string, code = 1): never {
381
+ console.error(`\n${message}`);
382
+ process.exit(code);
383
+ }
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ WORKSPACE_DIR="${OPENCODE_WORKSPACE_DIR:-${ROOT_DIR}/apps/server/data/workspace}"
6
+ OPENCODE_HOST="${OPENCODE_HOST:-127.0.0.1}"
7
+ OPENCODE_PORT="${OPENCODE_PORT:-4096}"
8
+
9
+ mkdir -p "${WORKSPACE_DIR}"
10
+ cd "${WORKSPACE_DIR}"
11
+
12
+ exec bun run "${ROOT_DIR}/vendor/opencode/packages/opencode/src/index.ts" \
13
+ serve \
14
+ --hostname "${OPENCODE_HOST}" \
15
+ --port "${OPENCODE_PORT}" \
16
+ --print-logs \
17
+ --log-level INFO
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ OPENCODE_HOST="${OPENCODE_HOST:-127.0.0.1}"
6
+ OPENCODE_PORT="${OPENCODE_PORT:-4096}"
7
+ OPENCODE_HEALTH_URL="http://${OPENCODE_HOST}:${OPENCODE_PORT}/global/health"
8
+ STARTUP_TIMEOUT_SECONDS="${OPENCODE_STARTUP_TIMEOUT_SECONDS:-30}"
9
+
10
+ cd "${ROOT_DIR}"
11
+
12
+ bun run build:app
13
+ concurrently -k -n opencode,server -c cyan,green \
14
+ "bun run dev:opencode" \
15
+ "bash -lc 'set -euo pipefail; echo \"[stack] waiting for OpenCode at ${OPENCODE_HEALTH_URL}\"; deadline=\$((SECONDS + ${STARTUP_TIMEOUT_SECONDS})); until curl -fsS \"${OPENCODE_HEALTH_URL}\" >/dev/null 2>&1; do if (( SECONDS >= deadline )); then echo \"[stack] timed out waiting for OpenCode health after ${STARTUP_TIMEOUT_SECONDS}s\"; exit 1; fi; sleep 0.25; done; echo \"[stack] OpenCode is healthy, starting Agent Mockingbird\"; exec bun run dev:server'"
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ WAFFLEBOT_PORT="${PORT:-${AGENT_MOCKINGBIRD_PORT:-3001}}"
6
+ DEV_WORKSPACE_DIR="${AGENT_MOCKINGBIRD_MEMORY_WORKSPACE_DIR:-${ROOT_DIR}/data/workspace}"
7
+ DEV_CONFIG_PATH="${AGENT_MOCKINGBIRD_CONFIG_PATH:-${ROOT_DIR}/data/agent-mockingbird.dev-stack.config.json}"
8
+ DEV_OPENCODE_CONFIG_DIR="${AGENT_MOCKINGBIRD_OPENCODE_CONFIG_DIR:-${ROOT_DIR}/data/opencode-config/dev-stack}"
9
+ DEV_WORKSPACE_ASSETS_STATE_PATH="${AGENT_MOCKINGBIRD_RUNTIME_WORKSPACE_ASSETS_STATE_PATH:-${ROOT_DIR}/data/runtime-assets-workspace.dev-stack.json}"
10
+ DEV_OPENCODE_ASSETS_STATE_PATH="${AGENT_MOCKINGBIRD_RUNTIME_OPENCODE_ASSETS_STATE_PATH:-${ROOT_DIR}/data/runtime-assets-opencode-config.dev-stack.json}"
11
+
12
+ if ! command -v bun >/dev/null 2>&1; then
13
+ echo "bun is required but not found in PATH."
14
+ exit 1
15
+ fi
16
+
17
+ echo "[stack] syncing runtime assets into ${DEV_WORKSPACE_DIR}"
18
+ (
19
+ cd "${ROOT_DIR}"
20
+ bun scripts/runtime-assets-sync.mjs \
21
+ --source "${ROOT_DIR}/runtime-assets/workspace" \
22
+ --target "${DEV_WORKSPACE_DIR}" \
23
+ --state "${DEV_WORKSPACE_ASSETS_STATE_PATH}" \
24
+ --mode install \
25
+ --non-interactive \
26
+ --quiet
27
+ )
28
+
29
+ echo "[stack] syncing OpenCode config assets into ${DEV_OPENCODE_CONFIG_DIR}"
30
+ (
31
+ cd "${ROOT_DIR}"
32
+ bun scripts/runtime-assets-sync.mjs \
33
+ --source "${ROOT_DIR}/runtime-assets/opencode-config" \
34
+ --target "${DEV_OPENCODE_CONFIG_DIR}" \
35
+ --state "${DEV_OPENCODE_ASSETS_STATE_PATH}" \
36
+ --mode install \
37
+ --non-interactive \
38
+ --quiet
39
+ )
40
+
41
+ if [[ -f "${DEV_OPENCODE_CONFIG_DIR}/package.json" ]]; then
42
+ echo "[stack] installing managed OpenCode config dependencies"
43
+ (
44
+ cd "${DEV_OPENCODE_CONFIG_DIR}"
45
+ bun install --frozen-lockfile >/dev/null
46
+ )
47
+ fi
48
+
49
+ echo "[stack] starting agent-mockingbird on http://127.0.0.1:${WAFFLEBOT_PORT}"
50
+ echo "[stack] workspace: ${DEV_WORKSPACE_DIR}"
51
+ echo "[stack] opencode-config: ${DEV_OPENCODE_CONFIG_DIR}"
52
+ echo "[stack] config: ${DEV_CONFIG_PATH}"
53
+
54
+ cd "${ROOT_DIR}"
55
+ export PORT="${WAFFLEBOT_PORT}"
56
+ export AGENT_MOCKINGBIRD_PORT="${WAFFLEBOT_PORT}"
57
+ export AGENT_MOCKINGBIRD_MEMORY_WORKSPACE_DIR="${DEV_WORKSPACE_DIR}"
58
+ export AGENT_MOCKINGBIRD_CONFIG_PATH="${DEV_CONFIG_PATH}"
59
+ export OPENCODE_CONFIG_DIR="${DEV_OPENCODE_CONFIG_DIR}"
60
+ export OPENCODE_DISABLE_PROJECT_CONFIG="1"
61
+ exec bun --hot apps/server/src/index.ts
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ if [[ "${EUID}" -ne 0 ]]; then
5
+ echo "Run as root (sudo)."
6
+ exit 1
7
+ fi
8
+
9
+ APP_USER="${AGENT_MOCKINGBIRD_USER:-agent-mockingbird}"
10
+ APP_GROUP="${AGENT_MOCKINGBIRD_GROUP:-${APP_USER}}"
11
+ APP_DIR="${AGENT_MOCKINGBIRD_APP_DIR:-/srv/agent-mockingbird/app}"
12
+ DATA_DIR="${AGENT_MOCKINGBIRD_DATA_DIR:-/var/lib/agent-mockingbird}"
13
+ OPENCODE_CONFIG_DIR="${AGENT_MOCKINGBIRD_OPENCODE_CONFIG_DIR:-${DATA_DIR}/opencode-config/systemd}"
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
15
+ UNIT_DIR="/etc/systemd/system"
16
+ TMP_DIR="$(mktemp -d)"
17
+
18
+ cleanup() {
19
+ rm -rf "${TMP_DIR}"
20
+ }
21
+ trap cleanup EXIT
22
+
23
+ require_cmd() {
24
+ local cmd="$1"
25
+ if ! command -v "${cmd}" >/dev/null 2>&1; then
26
+ echo "${cmd} is required but not installed."
27
+ exit 1
28
+ fi
29
+ }
30
+
31
+ require_cmd bun
32
+ require_cmd opencode
33
+ require_cmd systemctl
34
+ require_cmd tar
35
+
36
+ if ! getent group "${APP_GROUP}" >/dev/null 2>&1; then
37
+ groupadd --system "${APP_GROUP}"
38
+ fi
39
+
40
+ if ! id -u "${APP_USER}" >/dev/null 2>&1; then
41
+ useradd --system --home-dir "${APP_DIR}" --gid "${APP_GROUP}" --shell /usr/sbin/nologin "${APP_USER}"
42
+ fi
43
+
44
+ mkdir -p "${APP_DIR}" "${DATA_DIR}"
45
+
46
+ # Copy extracted release content into final app directory.
47
+ tar -C "${SCRIPT_DIR}" -cf - . | tar -C "${APP_DIR}" -xf -
48
+
49
+ chown -R "${APP_USER}:${APP_GROUP}" "${APP_DIR}" "${DATA_DIR}"
50
+
51
+ su -s /bin/bash -c "cd \"${APP_DIR}\" && bun install --frozen-lockfile" "${APP_USER}"
52
+ su -s /bin/bash -c "cd \"${APP_DIR}\" && bun scripts/runtime-assets-sync.mjs --source \"${APP_DIR}/runtime-assets/workspace\" --target \"${APP_DIR}\" --state \"${DATA_DIR}/runtime-assets-workspace-state.json\" --mode install --non-interactive" "${APP_USER}"
53
+ su -s /bin/bash -c "cd \"${APP_DIR}\" && bun scripts/runtime-assets-sync.mjs --source \"${APP_DIR}/runtime-assets/opencode-config\" --target \"${OPENCODE_CONFIG_DIR}\" --state \"${DATA_DIR}/runtime-assets-opencode-config-state.json\" --mode install --non-interactive" "${APP_USER}"
54
+ if [[ -f "${OPENCODE_CONFIG_DIR}/package.json" ]]; then
55
+ su -s /bin/bash -c "cd \"${OPENCODE_CONFIG_DIR}\" && bun install --frozen-lockfile" "${APP_USER}"
56
+ fi
57
+
58
+ render_unit() {
59
+ local src="$1"
60
+ local dst="$2"
61
+
62
+ sed \
63
+ -e "s|^User=.*$|User=${APP_USER}|" \
64
+ -e "s|^Group=.*$|Group=${APP_GROUP}|" \
65
+ -e "s|^WorkingDirectory=.*$|WorkingDirectory=${APP_DIR}|" \
66
+ -e "s|__AGENT_MOCKINGBIRD_OPENCODE_CONFIG_DIR__|${OPENCODE_CONFIG_DIR}|g" \
67
+ -e "s|/srv/agent-mockingbird/app|${APP_DIR}|g" \
68
+ -e "s|/var/lib/agent-mockingbird|${DATA_DIR}|g" \
69
+ "${src}" > "${dst}"
70
+ }
71
+
72
+ render_unit "${APP_DIR}/deploy/systemd/opencode.service" "${TMP_DIR}/opencode.service"
73
+ render_unit "${APP_DIR}/deploy/systemd/agent-mockingbird.service" "${TMP_DIR}/agent-mockingbird.service"
74
+
75
+ install -m 0644 "${TMP_DIR}/opencode.service" "${UNIT_DIR}/opencode.service"
76
+ install -m 0644 "${TMP_DIR}/agent-mockingbird.service" "${UNIT_DIR}/agent-mockingbird.service"
77
+
78
+ systemctl daemon-reload
79
+ systemctl enable --now opencode.service agent-mockingbird.service
80
+
81
+ echo "Installed agent-mockingbird with systemd services:"
82
+ echo " opencode.service"
83
+ echo " agent-mockingbird.service"
84
+ echo
85
+ echo "Health checks:"
86
+ echo " curl -sS http://127.0.0.1:3001/api/health"
87
+ echo " systemctl status agent-mockingbird.service --no-pager"
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ cd "${ROOT_DIR}"
6
+
7
+ if [[ -f ".env" ]]; then
8
+ while IFS= read -r line || [[ -n "${line}" ]]; do
9
+ if [[ "${line}" =~ ^[[:space:]]*# ]] || [[ "${line}" =~ ^[[:space:]]*$ ]]; then
10
+ continue
11
+ fi
12
+ if [[ "${line}" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
13
+ key="${BASH_REMATCH[1]}"
14
+ value="${BASH_REMATCH[2]}"
15
+ if [[ -z "${!key+x}" ]]; then
16
+ export "${key}=${value}"
17
+ fi
18
+ fi
19
+ done < ".env"
20
+ fi
21
+
22
+ if ! command -v bun >/dev/null 2>&1; then
23
+ echo "[memory:e2e] bun is required but not found in PATH."
24
+ exit 1
25
+ fi
26
+
27
+ if ! command -v curl >/dev/null 2>&1; then
28
+ echo "[memory:e2e] curl is required but not found in PATH."
29
+ exit 1
30
+ fi
31
+
32
+ MEMORY_ENABLED="${AGENT_MOCKINGBIRD_MEMORY_ENABLED:-true}"
33
+ EMBED_PROVIDER="${AGENT_MOCKINGBIRD_MEMORY_EMBED_PROVIDER:-ollama}"
34
+ OLLAMA_BASE_URL="${AGENT_MOCKINGBIRD_MEMORY_OLLAMA_BASE_URL:-http://127.0.0.1:11434}"
35
+ EMBED_MODEL="${AGENT_MOCKINGBIRD_MEMORY_EMBED_MODEL:-qwen3-embedding:4b}"
36
+
37
+ echo "[memory:e2e] starting"
38
+ echo "[memory:e2e] provider=${EMBED_PROVIDER} model=${EMBED_MODEL}"
39
+
40
+ if [[ "${MEMORY_ENABLED}" != "true" ]]; then
41
+ echo "[memory:e2e] AGENT_MOCKINGBIRD_MEMORY_ENABLED must be true."
42
+ exit 1
43
+ fi
44
+
45
+ if [[ "${EMBED_PROVIDER}" == "ollama" ]]; then
46
+ echo "[memory:e2e] checking ollama at ${OLLAMA_BASE_URL}"
47
+ if ! curl -fsS "${OLLAMA_BASE_URL}/api/tags" >/dev/null; then
48
+ echo "[memory:e2e] failed to reach Ollama tags endpoint."
49
+ echo "[memory:e2e] set AGENT_MOCKINGBIRD_MEMORY_OLLAMA_BASE_URL or run with AGENT_MOCKINGBIRD_MEMORY_EMBED_PROVIDER=none."
50
+ exit 1
51
+ fi
52
+ fi
53
+
54
+ echo "[memory:e2e] memory status"
55
+ bun run src/backend/memory/cli.ts status
56
+
57
+ echo "[memory:e2e] forcing reindex"
58
+ bun run src/backend/memory/cli.ts reindex
59
+
60
+ MARKER="memory-e2e-$(date +%s)-$RANDOM"
61
+ CONTENT="E2E marker ${MARKER} created at $(date -u +%Y-%m-%dT%H:%M:%SZ)"
62
+
63
+ echo "[memory:e2e] writing marker"
64
+ bun run src/backend/memory/cli.ts remember fact "${CONTENT}" >/tmp/agent-mockingbird-memory-e2e-remember.json
65
+
66
+ echo "[memory:e2e] searching marker"
67
+ SEARCH_OUTPUT="$(bun run src/backend/memory/cli.ts search "${MARKER}")"
68
+ printf '%s\n' "${SEARCH_OUTPUT}" >/tmp/agent-mockingbird-memory-e2e-search.json
69
+
70
+ if [[ "${SEARCH_OUTPUT}" != *"${MARKER}"* ]]; then
71
+ echo "[memory:e2e] marker was not found in retrieval output."
72
+ echo "[memory:e2e] wrote debug output to /tmp/agent-mockingbird-memory-e2e-search.json"
73
+ exit 1
74
+ fi
75
+
76
+ echo "[memory:e2e] PASS marker=${MARKER}"