feat: integrate 5 feature branches, upstream fixes, and MIME detection fix

Squashed 5 commits:

Features (from 5 feature branches):
- MCP fix, pipe mute, stub recovery
- KAIROS activation, openclaw autonomy
- Daemon/job command hierarchy + cross-platform bg engine

Upstream fixes:
- fix: Bun.hash compatibility
- chore: chrome dependency update
- docs: browser support guide

MIME detection fix:
- Screenshot detectMimeFromBase64(): decode raw bytes from base64
  instead of broken charCodeAt comparison
- Fixes API 400 on Windows (JPEG) and macOS (PNG) screenshots
This commit is contained in:
unraid
2026-04-14 18:32:19 +08:00
parent be80da4ce0
commit bddffa216a
93 changed files with 9653 additions and 1400 deletions

View File

@@ -72,6 +72,11 @@ import { QueryGuard } from '../utils/QueryGuard.js';
import { isEnvTruthy } from '../utils/envUtils.js';
import { formatTokens, truncateToWidth } from '../utils/format.js';
import { consumeEarlyInput } from '../utils/earlyInput.js';
import {
finalizeAutonomyRunCompleted,
finalizeAutonomyRunFailed,
markAutonomyRunRunning,
} from '../utils/autonomyRuns.js';
import { setMemberActive } from '../utils/swarm/teamHelpers.js';
import {
@@ -346,6 +351,9 @@ const usePipeRelay = feature('UDS_INBOX')
const usePipePermissionForward = feature('UDS_INBOX')
? require('../hooks/usePipePermissionForward.js').usePipePermissionForward
: () => undefined;
const usePipeMuteSync = feature('UDS_INBOX')
? require('../hooks/usePipeMuteSync.js').usePipeMuteSync
: () => undefined;
const usePipeRouter = feature('UDS_INBOX')
? require('../hooks/usePipeRouter.js').usePipeRouter
: () => ({ routeToSelectedPipes: () => false });
@@ -4299,7 +4307,7 @@ export function REPL({
});
}
} else {
injectUserMessageToTeammate(task.id, input, setAppState);
injectUserMessageToTeammate(task.id, input, undefined, setAppState);
}
setInputValue('');
helpers.setCursorOffset(0);
@@ -4804,7 +4812,10 @@ export function REPL({
// Submits incoming prompts from teammate messages or tasks mode as new turns
// Returns true if submission succeeded, false if a query is already running
const handleIncomingPrompt = useCallback(
(content: string, options?: { isMeta?: boolean }): boolean => {
(
input: string | QueuedCommand,
options?: { isMeta?: boolean },
): boolean => {
if (queryGuard.isActive) return false;
// Defer to user-queued commands — user input always takes priority
@@ -4816,16 +4827,53 @@ export function REPL({
return false;
}
const queuedCommand =
typeof input === 'string'
? ({
value: input,
mode: 'prompt',
isMeta: options?.isMeta ? true : undefined,
} satisfies QueuedCommand)
: input
const newAbortController = createAbortController();
setAbortController(newAbortController);
// Create a user message with the formatted content (includes XML wrapper)
const userMessage = createUserMessage({
content,
isMeta: options?.isMeta ? true : undefined,
content: queuedCommand.value as string,
isMeta: queuedCommand.isMeta ? true : undefined,
origin: queuedCommand.origin,
});
void onQuery([userMessage], newAbortController, true, [], mainLoopModel);
const autonomyRunId = queuedCommand.autonomy?.runId
if (autonomyRunId) {
void markAutonomyRunRunning(autonomyRunId)
}
void onQuery([userMessage], newAbortController, true, [], mainLoopModel)
.then(() => {
if (autonomyRunId) {
void finalizeAutonomyRunCompleted({
runId: autonomyRunId,
currentDir: getCwd(),
priority: 'later',
}).then(nextCommands => {
for (const command of nextCommands) {
enqueue(command);
}
})
}
})
.catch((error: unknown) => {
if (autonomyRunId) {
void finalizeAutonomyRunFailed({
runId: autonomyRunId,
error: String(error),
})
}
logError(toError(error))
})
return true;
},
[onQuery, mainLoopModel, store],
@@ -4856,6 +4904,7 @@ export function REPL({
const pipeIpcState = useAppState(s => getPipeIpc(s as any));
usePipePermissionForward({ store, tools, setMessages, setToolUseConfirmQueue, getToolUseContext, mainLoopModel });
usePipeMuteSync({ setToolUseConfirmQueue });
// Pipe IPC lifecycle — extracted to usePipeIpc hook
usePipeIpc({ store, handleIncomingPrompt });
@@ -4898,8 +4947,7 @@ export function REPL({
queuedCommandsLength: queuedCommands.length,
hasActiveLocalJsxUI: isShowingLocalJSXCommand,
isInPlanMode: toolPermissionContext.mode === 'plan',
onSubmitTick: (prompt: string) => handleIncomingPrompt(prompt, { isMeta: true }),
onQueueTick: (prompt: string) => enqueue({ mode: 'prompt', value: prompt, isMeta: true }),
onQueueTick: (command: QueuedCommand) => enqueue(command),
});
// Abort the current operation when a 'now' priority message arrives