@ouro.bot/cli 0.1.0-alpha.726 → 0.1.0-alpha.728
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 +14 -1
- package/assets/bluebubbles-host +264 -0
- package/changelog.json +18 -0
- package/dist/heart/config.js +1 -2
- package/dist/heart/core.js +181 -209
- package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
- package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
- package/dist/heart/daemon/bluebubbles-host.js +273 -0
- package/dist/heart/daemon/cli-defaults.js +8 -1
- package/dist/heart/daemon/cli-exec.js +239 -34
- package/dist/heart/daemon/cli-help.js +17 -6
- package/dist/heart/daemon/cli-parse.js +69 -1
- package/dist/heart/daemon/cli-render.js +7 -0
- package/dist/heart/daemon/daemon.js +79 -14
- package/dist/heart/daemon/doctor.js +21 -1
- package/dist/heart/daemon/mcp-canary.js +160 -29
- package/dist/heart/daemon/process-manager.js +100 -18
- package/dist/heart/daemon/socket-client.js +28 -19
- package/dist/heart/providers/anthropic.js +17 -12
- package/dist/heart/streaming.js +6 -6
- package/dist/heart/versioning/ouro-path-installer.js +30 -24
- package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
- package/dist/heart/versioning/version-intent.js +114 -0
- package/dist/mind/prompt-budget.js +104 -5
- package/dist/repertoire/tools.js +0 -10
- package/dist/senses/bluebubbles/client.js +43 -57
- package/dist/senses/bluebubbles/context-packet.js +104 -19
- package/dist/senses/bluebubbles/entry.js +14 -1
- package/dist/senses/bluebubbles/index.js +1398 -719
- package/dist/senses/bluebubbles/latest-turn.js +311 -0
- package/dist/senses/bluebubbles/outbound-state.js +4 -0
- package/dist/senses/bluebubbles/reaction-policy.js +4 -12
- package/dist/senses/bluebubbles/semantic-receipts.js +178 -10
- package/dist/senses/bluebubbles/webhook-registration.js +228 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -179,6 +179,9 @@ If you are changing runtime code, keep all three green.
|
|
|
179
179
|
```bash
|
|
180
180
|
ouro # open the interactive home deck in a human TTY
|
|
181
181
|
ouro up # start daemon from installed production version
|
|
182
|
+
ouro up --latest # preflight latest, then replace any exact rollback pin
|
|
183
|
+
ouro rollback <version> # pin normal starts to an exact installed version
|
|
184
|
+
ouro versions # show installed versions and current intent
|
|
182
185
|
ouro dev # start daemon from local repo build (auto-detects CWD)
|
|
183
186
|
ouro dev --repo-path /path # start from a specific repo checkout
|
|
184
187
|
ouro dev --clone # clone repo to ~/Projects/ouroboros, build, start
|
|
@@ -198,6 +201,7 @@ ouro connect perplexity --agent <name>
|
|
|
198
201
|
ouro connect embeddings --agent <name>
|
|
199
202
|
ouro connect teams --agent <name>
|
|
200
203
|
ouro connect bluebubbles --agent <name>
|
|
204
|
+
ouro bluebubbles host status --json
|
|
201
205
|
ouro connect voice --agent <name>
|
|
202
206
|
ouro auth --agent <name>
|
|
203
207
|
ouro auth --agent <name> --provider <provider>
|
|
@@ -218,11 +222,18 @@ ouro attention --agent <agent> # attention queue
|
|
|
218
222
|
ouro link <agent> --friend <id> --provider <provider> --external-id <external-id>
|
|
219
223
|
ouro setup --tool <tool> --agent <name> # register MCP server + hooks with a dev tool
|
|
220
224
|
ouro mcp-serve --agent <name> # start MCP server on stdin/stdout (used by dev tools)
|
|
225
|
+
ouro mcp doctor --agent <name> --json # bounded direct bridge evidence
|
|
221
226
|
ouro hook <event> --agent <name> # fire a lifecycle hook (SessionStart, Stop, PostToolUse)
|
|
222
227
|
```
|
|
223
228
|
|
|
224
229
|
The generic secret primitive is a vault item / credential in the owning agent vault: stable item name/path, hidden secret material, optional public fields, notes, timestamps/provenance, and no assumed use. `ouro connect` is for harness-managed workflows; workflow bindings reference ordinary vault items when they need secret material.
|
|
225
230
|
|
|
231
|
+
### Standard BlueBubbles setup
|
|
232
|
+
|
|
233
|
+
`ouro connect bluebubbles --agent <name>` is the standard local-Mac setup path. Besides saving the machine-scoped attachment, it installs or verifies the native-compatible BlueBubbles LaunchAgent for a same-user bridge and reconciles one Ouro-owned `[*]` webhook after the listener is bound. The daemon repairs that owned callback every 180 seconds, preserves unrelated callbacks, and creates the desired callback before removing a stale owned one. If the listener or BlueBubbles API is unavailable, connect says the attachment was saved but setup is incomplete; `ouro doctor` and `ouro bluebubbles host status --json` separate app, service, process, HTTP, and webhook failures.
|
|
234
|
+
|
|
235
|
+
When BlueBubbles runs in another logged-in macOS account, standard setup installs a generic helper and returns one nonce-bound `human-required` Terminal command for that account plus `ouro bluebubbles host collect --request-id <id>`. Ouro never asks for or stores the other account's login password. The receipt proves that one handoff and reports launchd only as point-in-time evidence; current process and HTTP health are checked separately.
|
|
236
|
+
|
|
226
237
|
## Setting Up On Another Machine
|
|
227
238
|
|
|
228
239
|
To clone an existing agent onto a new machine (macOS, Linux, or Windows via WSL2), see **[docs/cross-machine-setup.md](docs/cross-machine-setup.md)**. The short version is bundle plus vault: `npx ouro.bot@latest`, open the home deck, choose clone, enter the bundle's git remote URL, unlock the agent vault, refresh/verify credentials, and start with `ouro up`.
|
|
@@ -252,7 +263,9 @@ ouro setup --tool claude-code --agent <name>
|
|
|
252
263
|
ouro setup --tool codex --agent <name>
|
|
253
264
|
```
|
|
254
265
|
|
|
255
|
-
This registers the MCP server, installs lifecycle hooks (SessionStart, Stop, PostToolUse),
|
|
266
|
+
This registers the MCP server, installs lifecycle hooks (SessionStart, Stop, PostToolUse), detects dev vs installed mode automatically, and runs a bounded direct canary. Registration success and canary health are reported separately.
|
|
267
|
+
|
|
268
|
+
If the dev-tool host appears frozen, run `ouro mcp doctor --agent <name> --json`. Its classification is deliberately narrow: `ouro-bridge-failed`, `ouro-bridge-healthy-at-capture`, or `host-stall-unexplained`. Add `--host-stall-observed` only when the host stall was independently observed. A healthy bridge canary does not prove that Codex or another host caused the stall; it only bounds what Ouro observed at capture time.
|
|
256
269
|
|
|
257
270
|
**How it works:** When a developer starts a Claude Code session, the MCP server launches as a subprocess. The dev tool sees your MCP tools (`send_message`, `ask`, `check_response`, `status`, `search_facts`, `delegate`, etc.) and can invoke them mid-session. Conversation-shaped tools such as `send_message`, `ask`, `delegate`, `check_guidance`, and `report_progress` run full agent turns — you get your system prompt, your Desk record, your tools, everything. Read-only inspection tools such as `status` and `search_facts` do local lookup only. Missing `search_facts` hits are not evidence that the agent has no belief or preference.
|
|
258
271
|
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict"
|
|
3
|
+
|
|
4
|
+
const fs = require("fs")
|
|
5
|
+
const os = require("os")
|
|
6
|
+
const path = require("path")
|
|
7
|
+
const { execFileSync } = require("child_process")
|
|
8
|
+
|
|
9
|
+
const SCHEMA_VERSION = 1
|
|
10
|
+
const HELPER_VERSION = 1
|
|
11
|
+
const FRESHNESS_MS = 300000
|
|
12
|
+
const SHARED_ROOT = "/Users/Shared/Ouro"
|
|
13
|
+
const REQUESTS_ROOT = path.join(SHARED_ROOT, "bluebubbles-host-requests")
|
|
14
|
+
const RECEIPTS_ROOT = path.join(SHARED_ROOT, "bluebubbles-host-receipts")
|
|
15
|
+
const APP_PATH = "/Applications/BlueBubbles.app"
|
|
16
|
+
const EXECUTABLE_PATH = `${APP_PATH}/Contents/MacOS/BlueBubbles`
|
|
17
|
+
const LABEL = "com.bluebubbles.server"
|
|
18
|
+
|
|
19
|
+
function createRuntime(overrides = {}) {
|
|
20
|
+
return {
|
|
21
|
+
fs,
|
|
22
|
+
homedir: os.homedir,
|
|
23
|
+
now: Date.now,
|
|
24
|
+
getuid: () => process.getuid(),
|
|
25
|
+
execFileSync,
|
|
26
|
+
argv: process.argv,
|
|
27
|
+
writeStdout: (value) => process.stdout.write(value),
|
|
28
|
+
writeStderr: (value) => process.stderr.write(value),
|
|
29
|
+
setExitCode: (code) => { process.exitCode = code },
|
|
30
|
+
...overrides,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function fail(message, runtime) {
|
|
35
|
+
runtime.writeStderr(`bluebubbles-host: ${message}\n`)
|
|
36
|
+
runtime.setExitCode(1)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function runLaunchctl(args, runtime) {
|
|
40
|
+
try {
|
|
41
|
+
const output = runtime.execFileSync("launchctl", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] })
|
|
42
|
+
return { ok: true, detail: output.trim() || "ok" }
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return { ok: false, detail: error instanceof Error ? error.message : String(error) }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function requireLaunchctl(args, operation, runtime) {
|
|
49
|
+
const result = runLaunchctl(args, runtime)
|
|
50
|
+
if (result.ok) {
|
|
51
|
+
return result.detail
|
|
52
|
+
} else {
|
|
53
|
+
throw new Error(`launchctl ${operation} failed: ${result.detail}`)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function nativePlist() {
|
|
58
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
59
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
60
|
+
<plist version="1.0">
|
|
61
|
+
<dict>
|
|
62
|
+
<key>AssociatedBundleIdentifiers</key>
|
|
63
|
+
<array>
|
|
64
|
+
<string>com.BlueBubbles.BlueBubbles-Server</string>
|
|
65
|
+
</array>
|
|
66
|
+
<key>Label</key>
|
|
67
|
+
<string>com.bluebubbles.server</string>
|
|
68
|
+
<key>Program</key>
|
|
69
|
+
<string>${EXECUTABLE_PATH}</string>
|
|
70
|
+
<key>RunAtLoad</key>
|
|
71
|
+
<true/>
|
|
72
|
+
<key>KeepAlive</key>
|
|
73
|
+
<dict>
|
|
74
|
+
<key>SuccessfulExit</key>
|
|
75
|
+
<false/>
|
|
76
|
+
<key>Crashed</key>
|
|
77
|
+
<true/>
|
|
78
|
+
</dict>
|
|
79
|
+
</dict>
|
|
80
|
+
</plist>`
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function validateRequest(request, requestPath, runtime) {
|
|
84
|
+
if (path.dirname(path.resolve(requestPath)) !== REQUESTS_ROOT) throw new Error("request path is outside the shared request directory")
|
|
85
|
+
if (request.schemaVersion !== SCHEMA_VERSION) throw new Error("unsupported request schema")
|
|
86
|
+
if (request.helperVersion !== HELPER_VERSION) throw new Error("unsupported helper version")
|
|
87
|
+
if (!/^[A-Za-z_][A-Za-z0-9._-]*$/.test(request.username)) throw new Error("invalid target username")
|
|
88
|
+
if (!Number.isInteger(request.uid) || request.uid < 500) throw new Error("invalid target uid")
|
|
89
|
+
if (!/^[0-9a-f]{64}$/.test(request.nonce)) throw new Error("invalid request nonce")
|
|
90
|
+
if (request.requestId !== `${request.uid}-${request.nonce}`) throw new Error("request id mismatch")
|
|
91
|
+
if (!new Set(["install", "status", "repair", "remove"]).has(request.action)) throw new Error("unsupported host action")
|
|
92
|
+
const requestedAt = Date.parse(request.requestedAt)
|
|
93
|
+
const expiresAt = Date.parse(request.expiresAt)
|
|
94
|
+
const now = runtime.now()
|
|
95
|
+
if (!Number.isFinite(requestedAt) || !Number.isFinite(expiresAt)) throw new Error("invalid request time")
|
|
96
|
+
if (expiresAt - requestedAt > FRESHNESS_MS) throw new Error("request freshness window is too large")
|
|
97
|
+
if (requestedAt > now || now > expiresAt) throw new Error("request is not fresh")
|
|
98
|
+
|
|
99
|
+
const currentUid = runtime.getuid()
|
|
100
|
+
const currentUsername = runtime.execFileSync("id", ["-un"], { encoding: "utf8" }).trim()
|
|
101
|
+
if (currentUid !== request.uid || currentUsername !== request.username) throw new Error("run this request as its exact target user")
|
|
102
|
+
requireLaunchctl(["print", `gui/${currentUid}`], `print gui/${currentUid}`, runtime)
|
|
103
|
+
return request
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function inspect(request, runtime) {
|
|
107
|
+
const plistPath = path.join(runtime.homedir(), "Library", "LaunchAgents", `${LABEL}.plist`)
|
|
108
|
+
const serviceTarget = `gui/${request.uid}/${LABEL}`
|
|
109
|
+
const service = runLaunchctl(["print", serviceTarget], runtime)
|
|
110
|
+
const plist = runtime.fs.existsSync(plistPath)
|
|
111
|
+
? runtime.fs.readFileSync(plistPath, "utf8") === nativePlist() ? "current" : "drifted"
|
|
112
|
+
: "missing"
|
|
113
|
+
return { plistPath, serviceTarget, service, plist }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function receiptState(request, runtime) {
|
|
117
|
+
return {
|
|
118
|
+
plistPath: path.join(runtime.homedir(), "Library", "LaunchAgents", `${LABEL}.plist`),
|
|
119
|
+
serviceTarget: `gui/${request.uid}/${LABEL}`,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function requireApp(runtime) {
|
|
124
|
+
if (runtime.fs.existsSync(APP_PATH)) {
|
|
125
|
+
return APP_PATH
|
|
126
|
+
} else {
|
|
127
|
+
throw new Error(`BlueBubbles app is missing at ${APP_PATH}`)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function unloadService(state, disableFirst, runtime) {
|
|
132
|
+
if (!state.service.ok) {
|
|
133
|
+
return false
|
|
134
|
+
} else {
|
|
135
|
+
disableFirst && requireLaunchctl(["disable", state.serviceTarget], "disable", runtime)
|
|
136
|
+
requireLaunchctl(["bootout", state.serviceTarget], "bootout", runtime)
|
|
137
|
+
return true
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function installPlistIfNeeded(state, runtime) {
|
|
142
|
+
if (state.plist === "current") {
|
|
143
|
+
return false
|
|
144
|
+
} else {
|
|
145
|
+
runtime.fs.mkdirSync(path.dirname(state.plistPath), { recursive: true })
|
|
146
|
+
runtime.fs.writeFileSync(state.plistPath, nativePlist(), { mode: 0o644 })
|
|
147
|
+
return true
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function execute(request, runtime) {
|
|
152
|
+
const before = inspect(request, runtime)
|
|
153
|
+
if (request.action === "status") {
|
|
154
|
+
if (before.plist === "current" && before.service.ok) {
|
|
155
|
+
return before
|
|
156
|
+
} else {
|
|
157
|
+
throw new Error(`native service is not current and loaded (${before.plist}; ${before.service.detail})`)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (request.action === "remove") {
|
|
161
|
+
unloadService(before, true, runtime)
|
|
162
|
+
before.plist !== "missing" && runtime.fs.unlinkSync(before.plistPath)
|
|
163
|
+
const after = inspect(request, runtime)
|
|
164
|
+
if (after.plist !== "missing" || after.service.ok) throw new Error("native service removal did not verify")
|
|
165
|
+
return after
|
|
166
|
+
} else {
|
|
167
|
+
requireApp(runtime)
|
|
168
|
+
if (before.plist === "current" && before.service.ok) {
|
|
169
|
+
return before
|
|
170
|
+
} else {
|
|
171
|
+
installPlistIfNeeded(before, runtime)
|
|
172
|
+
unloadService(before, false, runtime)
|
|
173
|
+
requireLaunchctl(["disable", before.serviceTarget], "disable", runtime)
|
|
174
|
+
requireLaunchctl(["enable", before.serviceTarget], "enable", runtime)
|
|
175
|
+
requireLaunchctl(["bootstrap", `gui/${request.uid}`, before.plistPath], "bootstrap", runtime)
|
|
176
|
+
const after = inspect(request, runtime)
|
|
177
|
+
if (after.plist === "current" && after.service.ok) {
|
|
178
|
+
return after
|
|
179
|
+
} else {
|
|
180
|
+
throw new Error("native service installation did not verify")
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function publishReceipt(request, result, detail, state, runtime) {
|
|
187
|
+
const receipt = {
|
|
188
|
+
...request,
|
|
189
|
+
appPath: APP_PATH,
|
|
190
|
+
plistPath: state.plistPath,
|
|
191
|
+
launchAgentLabel: LABEL,
|
|
192
|
+
launchdDomain: `gui/${request.uid}`,
|
|
193
|
+
result,
|
|
194
|
+
detail,
|
|
195
|
+
verifiedAt: new Date(runtime.now()).toISOString(),
|
|
196
|
+
}
|
|
197
|
+
const finalPath = path.join(RECEIPTS_ROOT, `${request.requestId}.json`)
|
|
198
|
+
const temporaryPath = `${finalPath}.${request.nonce}.tmp`
|
|
199
|
+
runtime.fs.writeFileSync(temporaryPath, `${JSON.stringify(receipt, null, 2)}\n`, { encoding: "utf8", mode: 0o444, flag: "wx" })
|
|
200
|
+
try {
|
|
201
|
+
runtime.fs.chmodSync(temporaryPath, 0o444)
|
|
202
|
+
runtime.fs.linkSync(temporaryPath, finalPath)
|
|
203
|
+
runtime.fs.chmodSync(finalPath, 0o444)
|
|
204
|
+
} finally {
|
|
205
|
+
if (runtime.fs.existsSync(temporaryPath)) runtime.fs.unlinkSync(temporaryPath)
|
|
206
|
+
}
|
|
207
|
+
runtime.writeStdout(`${JSON.stringify({ ok: result === "verified", requestId: request.requestId, receiptPath: finalPath })}\n`)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function main(runtime) {
|
|
211
|
+
const requestFlag = runtime.argv.indexOf("--request")
|
|
212
|
+
if (requestFlag >= 0 && runtime.argv[requestFlag + 1]) {
|
|
213
|
+
const requestPath = path.resolve(runtime.argv[requestFlag + 1])
|
|
214
|
+
const request = validateRequest(JSON.parse(runtime.fs.readFileSync(requestPath, "utf8")), requestPath, runtime)
|
|
215
|
+
let state = receiptState(request, runtime)
|
|
216
|
+
try {
|
|
217
|
+
state = execute(request, runtime)
|
|
218
|
+
publishReceipt(request, "verified", `native ${request.action} verified`, state, runtime)
|
|
219
|
+
} catch (error) {
|
|
220
|
+
const detail = error instanceof Error ? error.message : String(error)
|
|
221
|
+
publishReceipt(request, "failed", detail, state, runtime)
|
|
222
|
+
throw error
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
throw new Error("usage: bluebubbles-host --request <request.json>")
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function runCli(runtime = createRuntime()) {
|
|
230
|
+
try {
|
|
231
|
+
main(runtime)
|
|
232
|
+
} catch (error) {
|
|
233
|
+
fail(error instanceof Error ? error.message : String(error), runtime)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function runCliIfMain(isMain, runtime) {
|
|
238
|
+
if (isMain) {
|
|
239
|
+
runCli(runtime)
|
|
240
|
+
} else {
|
|
241
|
+
return false
|
|
242
|
+
}
|
|
243
|
+
return true
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
runCliIfMain(require.main === module)
|
|
247
|
+
|
|
248
|
+
module.exports = {
|
|
249
|
+
constants: {
|
|
250
|
+
schemaVersion: SCHEMA_VERSION,
|
|
251
|
+
helperVersion: HELPER_VERSION,
|
|
252
|
+
freshnessMs: FRESHNESS_MS,
|
|
253
|
+
sharedRoot: SHARED_ROOT,
|
|
254
|
+
},
|
|
255
|
+
createRuntime,
|
|
256
|
+
nativePlist,
|
|
257
|
+
validateRequest,
|
|
258
|
+
inspect,
|
|
259
|
+
execute,
|
|
260
|
+
publishReceipt,
|
|
261
|
+
main,
|
|
262
|
+
runCli,
|
|
263
|
+
runCliIfMain,
|
|
264
|
+
}
|
package/changelog.json
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.728",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Make exact rollback intent durable, bound status and MCP diagnostics, and make BlueBubbles host continuity plus owner-safe webhook reconciliation part of standard setup.",
|
|
8
|
+
"Verify the packed BlueBubbles host helper and exact wrapper intent across fresh, older, equal, and newer installations, and replace the vulnerable mail parsing dependency chain.",
|
|
9
|
+
"Enforce status as an absolute wall-clock deadline even under trickled socket data, and keep sanitized MCP failure evidence compliant with the runtime logging schema.",
|
|
10
|
+
"Stage published latest targets in either direction, verify the recovery launcher before version-intent commits, and require BlueBubbles host repair to finish with a current loaded LaunchAgent."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"version": "0.1.0-alpha.727",
|
|
15
|
+
"changes": [
|
|
16
|
+
"Keep BlueBubbles replies on the latest turn with pre-listen recovery epochs, conservative unresolved-route fences, retained retry generations, and durable inbound-reply send boundaries that suppress crash retries.",
|
|
17
|
+
"Preserve full anchor-inclusive context pages and the exact verified predecessor; block unadvertised mid-turn speak calls; isolate provider state; validate terminal payloads; cancel stale activity transports; and keep reactions and progress mutations capture-only with complete causal-race and transport-failure coverage.",
|
|
18
|
+
"Recheck latest-turn authority at the final synchronous transport boundary, retry temporary observation fences without pre-reserving a send, retain canonical projection after accepted-send bookkeeping failures, and persist causal order in a rollback-safe fingerprinted sidecar while keeping .726 capture and handled records readable.",
|
|
19
|
+
"Bind startup and periodic catch-up ranges to one upstream timestamp snapshot; keep runtime sync single-flight; recheck shutdown and supersession after asynchronous turn setup; close the managed SIGTERM/SIGINT lifecycle gate; terminally fence starts during whole-daemon shutdown; drain every worker before replacement; and refuse startup while any verified old Ouro process, including Voice, survives cleanup."
|
|
20
|
+
]
|
|
21
|
+
},
|
|
4
22
|
{
|
|
5
23
|
"version": "0.1.0-alpha.726",
|
|
6
24
|
"changes": [
|
package/dist/heart/config.js
CHANGED
|
@@ -315,8 +315,7 @@ function getBlueBubblesConfig() {
|
|
|
315
315
|
}
|
|
316
316
|
function getBlueBubblesChannelConfig() {
|
|
317
317
|
const config = loadConfig();
|
|
318
|
-
|
|
319
|
-
return { port, webhookPath, requestTimeoutMs };
|
|
318
|
+
return { ...config.bluebubblesChannel };
|
|
320
319
|
}
|
|
321
320
|
function getIntegrationsConfig() {
|
|
322
321
|
const config = loadConfig();
|