feat: refine subagents + add chat.inject
Co-authored-by: Tyler Yust <tyler6204@users.noreply.github.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import { resolveThinkingDefault } from "../../agents/model-selection.js";
|
||||
import { resolveAgentTimeoutMs } from "../../agents/timeout.js";
|
||||
import { agentCommand } from "../../commands/agent.js";
|
||||
import { mergeSessionEntry, saveSessionStore } from "../../config/sessions.js";
|
||||
import { mergeSessionEntry, updateSessionStore } from "../../config/sessions.js";
|
||||
import { registerAgentRunContext } from "../../infra/agent-events.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { resolveSendPolicy } from "../../sessions/send-policy.js";
|
||||
@@ -21,6 +23,7 @@ import {
|
||||
formatValidationErrors,
|
||||
validateChatAbortParams,
|
||||
validateChatHistoryParams,
|
||||
validateChatInjectParams,
|
||||
validateChatSendParams,
|
||||
} from "../protocol/index.js";
|
||||
import { MAX_CHAT_HISTORY_MESSAGES_BYTES } from "../server-constants.js";
|
||||
@@ -205,7 +208,7 @@ export const chatHandlers: GatewayRequestHandlers = {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const { cfg, storePath, store, entry, canonicalKey } = loadSessionEntry(p.sessionKey);
|
||||
const { cfg, storePath, entry, canonicalKey } = loadSessionEntry(p.sessionKey);
|
||||
const timeoutMs = resolveAgentTimeoutMs({
|
||||
cfg,
|
||||
overrideMs: p.timeoutMs,
|
||||
@@ -284,11 +287,10 @@ export const chatHandlers: GatewayRequestHandlers = {
|
||||
clientRunId,
|
||||
});
|
||||
|
||||
if (store) {
|
||||
store[canonicalKey] = sessionEntry;
|
||||
if (storePath) {
|
||||
await saveSessionStore(storePath, store);
|
||||
}
|
||||
if (storePath) {
|
||||
await updateSessionStore(storePath, (store) => {
|
||||
store[canonicalKey] = sessionEntry;
|
||||
});
|
||||
}
|
||||
|
||||
const ackPayload = {
|
||||
@@ -355,4 +357,80 @@ export const chatHandlers: GatewayRequestHandlers = {
|
||||
});
|
||||
}
|
||||
},
|
||||
"chat.inject": async ({ params, respond, context }) => {
|
||||
if (!validateChatInjectParams(params)) {
|
||||
respond(
|
||||
false,
|
||||
undefined,
|
||||
errorShape(
|
||||
ErrorCodes.INVALID_REQUEST,
|
||||
`invalid chat.inject params: ${formatValidationErrors(validateChatInjectParams.errors)}`,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
const p = params as {
|
||||
sessionKey: string;
|
||||
message: string;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
// Load session to find transcript file
|
||||
const { storePath, entry } = loadSessionEntry(p.sessionKey);
|
||||
const sessionId = entry?.sessionId;
|
||||
if (!sessionId || !storePath) {
|
||||
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "session not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve transcript path
|
||||
const transcriptPath = entry?.sessionFile
|
||||
? entry.sessionFile
|
||||
: path.join(path.dirname(storePath), `${sessionId}.jsonl`);
|
||||
|
||||
if (!fs.existsSync(transcriptPath)) {
|
||||
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "transcript file not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Build transcript entry
|
||||
const now = Date.now();
|
||||
const messageId = randomUUID().slice(0, 8);
|
||||
const labelPrefix = p.label ? `[${p.label}]\n\n` : "";
|
||||
const messageBody: Record<string, unknown> = {
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: `${labelPrefix}${p.message}` }],
|
||||
timestamp: now,
|
||||
stopReason: "injected",
|
||||
usage: { input: 0, output: 0, totalTokens: 0 },
|
||||
};
|
||||
const transcriptEntry = {
|
||||
type: "message",
|
||||
id: messageId,
|
||||
timestamp: new Date(now).toISOString(),
|
||||
message: messageBody,
|
||||
};
|
||||
|
||||
// Append to transcript file
|
||||
try {
|
||||
fs.appendFileSync(transcriptPath, `${JSON.stringify(transcriptEntry)}\n`, "utf-8");
|
||||
} catch (err) {
|
||||
const errMessage = err instanceof Error ? err.message : String(err);
|
||||
respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, `failed to write transcript: ${errMessage}`));
|
||||
return;
|
||||
}
|
||||
|
||||
// Broadcast to webchat for immediate UI update
|
||||
const chatPayload = {
|
||||
runId: `inject-${messageId}`,
|
||||
sessionKey: p.sessionKey,
|
||||
seq: 0,
|
||||
state: "final" as const,
|
||||
message: transcriptEntry.message,
|
||||
};
|
||||
context.broadcast("chat", chatPayload);
|
||||
context.bridgeSendToSession(p.sessionKey, "chat", chatPayload);
|
||||
|
||||
respond(true, { ok: true, messageId });
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user