merge: integrate origin/main (406 commits) into live branch
Merge upstream's paired-room stabilization, dedicated message-runtime-loop, outbound delivery refactor (ipc-outbound-delivery / deliverFormattedCanonicalMessage), discord module split, dashboard rework, and migrations 015-018 into our branch. Conflict policy: prefer upstream for the heavily-refactored paired-room / message-runtime / db cluster (it supersedes our earlier loop band-aids), keep only orthogonal unique fixes: - codex bundled-JS launcher fix (codex-warmup.ts) - slot-aligned Claude+Codex usage primer (usage-primer.ts) re-wired into index.ts - MemoryHigh=3G cgroup bound, discord perms diagnostic, prompt docs - progress null-race + silent-failure publish guards (message-turn-controller.ts) Dropped (superseded by upstream or unwired): reviewer STEP_DONE/TASK_DONE loop band-aids, router markdown-escape override, register tribunal-default+git-init, restart-context rewindToSeq (unwired). Verified: typecheck clean, build clean, vitest shows zero new regressions vs the origin/main baseline (only pre-existing env-config failures remain).
This commit is contained in:
@@ -22,6 +22,11 @@ interface RunSpawnedAgentProcessArgs {
|
||||
logsDir: string;
|
||||
startTime: number;
|
||||
onOutput?: (output: AgentOutput) => Promise<void>;
|
||||
onTerminalStreamedOutputFlushed?: (output: AgentOutput) => void;
|
||||
}
|
||||
|
||||
function isTerminalStreamedOutput(output: AgentOutput): boolean {
|
||||
return (output.phase ?? 'final') !== 'progress';
|
||||
}
|
||||
|
||||
function parseLegacyAgentOutput(stdout: string): AgentOutput {
|
||||
@@ -39,6 +44,88 @@ function parseLegacyAgentOutput(stdout: string): AgentOutput {
|
||||
return JSON.parse(jsonLine) as AgentOutput;
|
||||
}
|
||||
|
||||
function logStreamedOutputDeliveryError(
|
||||
err: unknown,
|
||||
group: RegisteredGroup,
|
||||
input: AgentInput,
|
||||
): void {
|
||||
logger.warn(
|
||||
{
|
||||
group: group.name,
|
||||
chatJid: input.chatJid,
|
||||
runId: input.runId,
|
||||
error: getErrorMessage(err),
|
||||
},
|
||||
'Streamed agent output delivery failed',
|
||||
);
|
||||
}
|
||||
|
||||
function logStreamedAgentErrorOutput(
|
||||
output: AgentOutput,
|
||||
group: RegisteredGroup,
|
||||
input: AgentInput,
|
||||
): void {
|
||||
if (output.status !== 'error') return;
|
||||
logger.warn(
|
||||
{
|
||||
group: group.name,
|
||||
chatJid: input.chatJid,
|
||||
runId: input.runId,
|
||||
error: output.error,
|
||||
newSessionId: output.newSessionId,
|
||||
},
|
||||
'Streamed agent error output',
|
||||
);
|
||||
}
|
||||
|
||||
function chainStreamedOutputDelivery(args: {
|
||||
outputChain: Promise<void>;
|
||||
parsed: AgentOutput;
|
||||
onOutput: (output: AgentOutput) => Promise<void>;
|
||||
onTerminalStreamedOutputFlushed?: (output: AgentOutput) => void;
|
||||
group: RegisteredGroup;
|
||||
input: AgentInput;
|
||||
}): Promise<void> {
|
||||
return args.outputChain.then(async () => {
|
||||
try {
|
||||
await args.onOutput(args.parsed);
|
||||
if (isTerminalStreamedOutput(args.parsed)) {
|
||||
args.onTerminalStreamedOutputFlushed?.(args.parsed);
|
||||
}
|
||||
} catch (err) {
|
||||
logStreamedOutputDeliveryError(err, args.group, args.input);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeTimeoutLog(args: {
|
||||
logsDir: string;
|
||||
input: AgentInput;
|
||||
group: RegisteredGroup;
|
||||
processName: string;
|
||||
duration: number;
|
||||
code: number | null;
|
||||
signal: NodeJS.Signals | null;
|
||||
hadStreamingOutput: boolean;
|
||||
}): void {
|
||||
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
fs.writeFileSync(
|
||||
path.join(args.logsDir, `agent-${args.input.runId || 'adhoc'}-${ts}.log`),
|
||||
[
|
||||
`=== Agent Run Log (TIMEOUT) ===`,
|
||||
`Timestamp: ${new Date().toISOString()}`,
|
||||
`Group: ${args.group.name}`,
|
||||
`ChatJid: ${args.input.chatJid}`,
|
||||
`RunId: ${args.input.runId || 'n/a'}`,
|
||||
`Process: ${args.processName}`,
|
||||
`Duration: ${args.duration}ms`,
|
||||
`Exit Code: ${args.code}`,
|
||||
`Signal: ${args.signal}`,
|
||||
`Had Streaming Output: ${args.hadStreamingOutput}`,
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
export function runSpawnedAgentProcess(
|
||||
args: RunSpawnedAgentProcessArgs,
|
||||
): Promise<AgentOutput> {
|
||||
@@ -144,19 +231,16 @@ export function runSpawnedAgentProcess(
|
||||
}
|
||||
hadStreamingOutput = true;
|
||||
resetTimeout();
|
||||
if (parsed.status === 'error') {
|
||||
logger.warn(
|
||||
{
|
||||
group: group.name,
|
||||
chatJid: input.chatJid,
|
||||
runId: input.runId,
|
||||
error: parsed.error,
|
||||
newSessionId: parsed.newSessionId,
|
||||
},
|
||||
'Streamed agent error output',
|
||||
);
|
||||
}
|
||||
outputChain = outputChain.then(() => onOutput(parsed));
|
||||
logStreamedAgentErrorOutput(parsed, group, input);
|
||||
outputChain = chainStreamedOutputDelivery({
|
||||
outputChain,
|
||||
parsed,
|
||||
onOutput,
|
||||
onTerminalStreamedOutputFlushed:
|
||||
args.onTerminalStreamedOutputFlushed,
|
||||
group,
|
||||
input,
|
||||
});
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
{
|
||||
@@ -215,22 +299,16 @@ export function runSpawnedAgentProcess(
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
if (timedOut) {
|
||||
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
fs.writeFileSync(
|
||||
path.join(logsDir, `agent-${input.runId || 'adhoc'}-${ts}.log`),
|
||||
[
|
||||
`=== Agent Run Log (TIMEOUT) ===`,
|
||||
`Timestamp: ${new Date().toISOString()}`,
|
||||
`Group: ${group.name}`,
|
||||
`ChatJid: ${input.chatJid}`,
|
||||
`RunId: ${input.runId || 'n/a'}`,
|
||||
`Process: ${processName}`,
|
||||
`Duration: ${duration}ms`,
|
||||
`Exit Code: ${code}`,
|
||||
`Signal: ${signal}`,
|
||||
`Had Streaming Output: ${hadStreamingOutput}`,
|
||||
].join('\n'),
|
||||
);
|
||||
writeTimeoutLog({
|
||||
logsDir,
|
||||
input,
|
||||
group,
|
||||
processName,
|
||||
duration,
|
||||
code,
|
||||
signal,
|
||||
hadStreamingOutput,
|
||||
});
|
||||
|
||||
if (hadStreamingOutput) {
|
||||
logger.info(
|
||||
|
||||
Reference in New Issue
Block a user