feat: add host commander skeleton

This commit is contained in:
Codex
2026-05-21 12:28:13 +00:00
parent 9f166d0580
commit 67f6d0e820
16 changed files with 1187 additions and 277 deletions
@@ -0,0 +1,11 @@
FROM oven/bun:1-alpine
WORKDIR /app/src/components/microservices/host-codex-commander
COPY src/components/microservices/host-codex-commander/package.json ./package.json
RUN bun install --production
COPY src/components/microservices/host-codex-commander/tsconfig.json ./tsconfig.json
COPY src/components/shared /app/src/components/shared
COPY src/components/microservices/host-codex-commander/src ./src
EXPOSE 4261
CMD ["bun", "run", "src/index.ts"]
@@ -0,0 +1,15 @@
{
"name": "@unidesk/host-codex-commander",
"private": true,
"type": "module",
"scripts": {
"start": "bun run src/index.ts",
"check": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {},
"devDependencies": {
"@types/bun": "latest",
"@types/node": "latest",
"typescript": "latest"
}
}
@@ -0,0 +1,141 @@
export const commanderServiceId = "host-codex-commander";
export const commanderPhase = "source-contract";
export const commanderHighRiskActions = [
"code-queue-backend-restart",
"code-queue-backend-rebuild",
"code-queue-execution-plane-restart",
"code-queue-task-interrupt",
"code-queue-task-cancel",
"prod-runtime-mutation",
] as const;
export type CommanderHighRiskAction = typeof commanderHighRiskActions[number];
export type CommanderSessionState =
| "unknown"
| "discovered"
| "planned"
| "starting"
| "running"
| "attention_required"
| "stopping"
| "stopped"
| "degraded";
export type CommanderPromptState =
| "draft"
| "planned"
| "queued_for_injection"
| "injected"
| "rejected"
| "failed";
export type CommanderApprovalState =
| "draft"
| "requested"
| "approved"
| "rejected"
| "expired"
| "consumed";
export interface CommanderContract {
ok: true;
phase: typeof commanderPhase;
serviceId: typeof commanderServiceId;
currentImplementation: "host-codex-commander-skeleton";
daemonImplemented: false;
liveOperationsImplemented: false;
purpose: string;
ownershipBoundary: {
hostCodexProcess: string;
controlMicroservice: string;
codeQueue: string;
claudeqq: string;
};
requiredCapabilities: string[];
apiContract: {
health: string;
contract: string;
sessions: string;
sessionPlan: string;
promptPlan: string;
traceSummary: string;
issueWritePlan: string;
approvalRequest: string;
};
stateModel: {
sessionStates: CommanderSessionState[];
promptStates: CommanderPromptState[];
approvalStates: CommanderApprovalState[];
storageRoot: ".state/commander/";
redactionPolicy: string;
};
safetyBoundary: {
phaseOneMutationAllowed: false;
forbiddenWithoutExplicitUserApproval: CommanderHighRiskAction[];
alwaysForbidden: string[];
confirmationPolicy: string;
};
}
export function commanderContract(): CommanderContract {
return {
ok: true,
phase: commanderPhase,
serviceId: commanderServiceId,
currentImplementation: "host-codex-commander-skeleton",
daemonImplemented: false,
liveOperationsImplemented: false,
purpose: "Keep host Codex supervision observable through a local-only skeleton that can persist state, summarize traces, and draft approvals without touching live bridges.",
ownershipBoundary: {
hostCodexProcess: "Long-lived Codex process on the master server host.",
controlMicroservice: "Local skeleton for health, state, trace summary, and approval drafting with no live bridge or executor.",
codeQueue: "Execution plane remains separate and is never restarted or attached by this skeleton.",
claudeqq: "Approval draft destination only; no messages are sent from this stage.",
},
requiredCapabilities: [
"host-codex-process-discovery",
"host-codex-start-plan",
"ssh-bridge-contract",
"pty-bridge-contract",
"stdio-bridge-contract",
"prompt-guidance-plan",
"trace-summary-plan",
"issue-20-board-read-write-entry",
"issue-46-brief-read-write-entry",
"claudeqq-high-risk-approval-entry",
],
apiContract: {
health: "GET /health",
contract: "GET /api/commander/contract",
sessions: "GET /api/commander/sessions",
sessionPlan: "POST /api/commander/sessions/:sessionId/plan-start",
promptPlan: "POST /api/commander/sessions/:sessionId/prompt-plan",
traceSummary: "GET /api/commander/trace-summary",
issueWritePlan: "POST /api/commander/issues/:issueNumber/write-plan",
approvalRequest: "POST /api/commander/approvals",
},
stateModel: {
sessionStates: ["unknown", "discovered", "planned", "starting", "running", "attention_required", "stopping", "stopped", "degraded"],
promptStates: ["draft", "planned", "queued_for_injection", "injected", "rejected", "failed"],
approvalStates: ["draft", "requested", "approved", "rejected", "expired", "consumed"],
storageRoot: ".state/commander/",
redactionPolicy: "Never persist or print token, secret, password, key, cookie, authorization, or credential URLs in cleartext.",
},
safetyBoundary: {
phaseOneMutationAllowed: false,
forbiddenWithoutExplicitUserApproval: commanderHighRiskActions,
alwaysForbidden: [
"print-token-values",
"read-token-files-for-display",
"direct-database-state-patch",
"bypass-code-queue-backend-confirmation-policy",
"replace-code-queue-runner",
"deploy-or-restart-production-runtime-from-this-contract-stub",
],
confirmationPolicy: "High-risk actions must draft a ClaudeQQ request, wait for explicit user approval, bind approval to one exact action, and record the decision before any future live executor may proceed.",
},
};
}
@@ -0,0 +1,283 @@
import { existsSync, mkdirSync } from "node:fs";
import { dirname } from "node:path";
import { createHourlyJsonlWriter, logRetentionBytesForService } from "../../../shared/src/rotating-jsonl";
import { commanderContract } from "./contract";
import { commanderHighRiskActions } from "./contract";
import {
appendCommanderTraceEvent,
buildCommanderApprovalDraft,
commanderApprovalPreview,
commanderHealth,
commanderSessionPreview,
commanderStatePaths,
listCommanderSessions,
normalizeCommanderApprovalState,
normalizeCommanderPromptState,
normalizeCommanderSessionState,
readCommanderApproval,
readCommanderSession,
writeCommanderApproval,
writeCommanderSession,
summarizeCommanderTrace,
type CommanderStorageConfig,
type CommanderApprovalDraftRecord,
type CommanderSessionRecord,
} from "./state";
export interface RuntimeConfig extends CommanderStorageConfig {
host: string;
port: number;
logFile: string;
serviceId: string;
stateRoot: string;
sessionId: string;
}
type JsonRecord = Record<string, unknown>;
const startedAt = new Date().toISOString();
const recentLogs: JsonRecord[] = [];
const isCheckMode = process.execArgv.includes("--check");
function envString(name: string, fallback: string): string {
const value = process.env[name];
return value === undefined || value.length === 0 ? fallback : value;
}
function envNumber(name: string, fallback: number): number {
const raw = process.env[name];
if (raw === undefined || raw.trim().length === 0) return fallback;
const parsed = Number(raw);
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
}
function buildConfig(): RuntimeConfig {
const stateRoot = envString("COMMANDER_STATE_ROOT", "/var/lib/unidesk/commander");
const logFile = envString("LOG_FILE", "/var/log/unidesk/commander.jsonl");
return {
rootDir: stateRoot,
host: envString("HOST", "0.0.0.0"),
port: envNumber("PORT", 4261),
logFile,
serviceId: "host-codex-commander",
stateRoot,
sessionId: envString("COMMANDER_SESSION_ID", "primary"),
};
}
let config = buildConfig();
let logWriter: ReturnType<typeof createHourlyJsonlWriter> | null = null;
export function setCommanderRuntimeConfig(next: RuntimeConfig): void {
config = next;
logWriter = null;
}
function getLogWriter(): ReturnType<typeof createHourlyJsonlWriter> {
if (logWriter === null) {
mkdirSync(dirname(config.logFile), { recursive: true });
mkdirSync(config.stateRoot, { recursive: true });
logWriter = createHourlyJsonlWriter({
baseLogFile: config.logFile,
service: "host-codex-commander",
maxBytes: logRetentionBytesForService("host-codex-commander"),
});
logWriter.prune();
}
return logWriter;
}
function log(event: string, detail: JsonRecord = {}): void {
const record: JsonRecord = { at: new Date().toISOString(), service: config.serviceId, event, ...detail };
recentLogs.push(record);
while (recentLogs.length > 200) recentLogs.shift();
try {
getLogWriter().appendJson(record, new Date(String(record.at)));
} catch {
// Logging must never block the skeleton service.
}
console.log(JSON.stringify(record));
}
function jsonResponse(body: unknown, status = 200, headers: Record<string, string> = {}): Response {
return new Response(JSON.stringify(body), {
status,
headers: { "content-type": "application/json; charset=utf-8", ...headers },
});
}
function errorToJson(error: unknown): JsonRecord {
if (error instanceof Error) return { name: error.name, message: error.message, stack: error.stack ?? "" };
return { message: String(error) };
}
function errorResponse(error: unknown, status = 500): Response {
log("request_failed", { status, error: errorToJson(error) });
return jsonResponse({ ok: false, error: error instanceof Error ? error.message : String(error) }, status);
}
function readJsonBody(body: Request): Promise<unknown> {
return body.json();
}
function routePath(url: URL): string {
return url.pathname.replace(/\/+$/u, "") || "/";
}
function commanderInfo(): JsonRecord {
const paths = commanderStatePaths(config);
const session = readCommanderSession(config, config.sessionId);
return {
ok: true,
service: config.serviceId,
serviceId: config.serviceId,
status: "healthy",
startedAt,
config: {
host: config.host,
port: config.port,
stateRoot: config.stateRoot,
logFile: config.logFile,
sessionId: config.sessionId,
},
state: commanderSessionPreview(session),
files: {
stateFile: paths.stateFile,
approvalFile: paths.approvalFile,
traceFile: paths.traceFile,
logFile: paths.logFile,
stateRootExists: existsSync(config.stateRoot),
},
};
}
export function createCommanderRequestHandler(runtimeConfig: RuntimeConfig): (req: Request) => Promise<Response> | Response {
return async (req: Request) => {
const previousConfig = config;
config = runtimeConfig;
try {
return await Promise.resolve(handleCommanderRequest(req));
} finally {
config = previousConfig;
}
};
}
function normalizeSessionUpdate(input: unknown): CommanderSessionRecord {
const body = typeof input === "object" && input !== null && !Array.isArray(input) ? input as Record<string, unknown> : {};
const current = readCommanderSession(config, typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : config.sessionId);
current.state = typeof body.state === "string" ? normalizeCommanderSessionState(body.state) : current.state;
current.promptState = typeof body.promptState === "string" ? normalizeCommanderPromptState(body.promptState) : current.promptState;
current.approvalState = typeof body.approvalState === "string" ? normalizeCommanderApprovalState(body.approvalState) : current.approvalState;
current.pid = typeof body.pid === "number" && Number.isFinite(body.pid) ? Math.floor(body.pid) : current.pid;
current.cwd = typeof body.cwd === "string" ? body.cwd : current.cwd;
current.lastSeq = typeof body.lastSeq === "number" && Number.isFinite(body.lastSeq) ? Math.max(0, Math.floor(body.lastSeq)) : current.lastSeq;
current.heartbeatAt = typeof body.heartbeatAt === "string" ? body.heartbeatAt : current.heartbeatAt;
current.updatedAt = new Date().toISOString();
if (Array.isArray(body.notes)) {
current.notes = body.notes.filter((item): item is string => typeof item === "string");
}
return writeCommanderSession(config, current);
}
function updateSessionFromTrace(input: unknown): JsonRecord {
const body = typeof input === "object" && input !== null && !Array.isArray(input) ? input as Record<string, unknown> : {};
const sessionId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : config.sessionId;
const traceJsonl = typeof body.traceJsonl === "string" ? body.traceJsonl : "";
const taskSummary = typeof body.taskSummary === "string" ? body.taskSummary : null;
const taskId = typeof body.taskId === "string" ? body.taskId : null;
const summary = summarizeCommanderTrace({ taskId, sessionId, traceJsonl, taskSummary });
appendCommanderTraceEvent(config, sessionId, {
type: "trace-summary-dry-run",
taskId,
summary,
});
const nextState = summary.status === "terminal"
? "stopped"
: summary.status === "attention_required"
? "attention_required"
: summary.status === "blocked"
? "degraded"
: "running";
const session = writeCommanderSession(config, {
...readCommanderSession(config, sessionId),
sessionId,
state: nextState,
promptState: summary.status === "attention_required" ? "planned" : "draft",
approvalState: summary.status === "attention_required" ? "requested" : "draft",
lastSeq: summary.lastSeq,
heartbeatAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
notes: [...readCommanderSession(config, sessionId).notes, `trace-summary:${summary.status}`].slice(-20),
pid: readCommanderSession(config, sessionId).pid,
cwd: readCommanderSession(config, sessionId).cwd,
});
return { ok: true, summary, session: commanderSessionPreview(session) };
}
function writeApprovalDraft(input: unknown): JsonRecord {
const body = typeof input === "object" && input !== null && !Array.isArray(input) ? input as Record<string, unknown> : {};
const action = typeof body.action === "string" ? body.action : "unknown";
if (!commanderHighRiskActions.includes(action as typeof commanderHighRiskActions[number])) {
return { ok: false, error: "validation-failed", message: `unsupported high-risk action: ${action}`, highRiskActions: commanderHighRiskActions };
}
const taskId = typeof body.taskId === "string" ? body.taskId : null;
const reason = typeof body.reason === "string" ? body.reason : "";
const approvalId = typeof body.approvalId === "string" && body.approvalId.length > 0 ? body.approvalId : "draft";
const draft = buildCommanderApprovalDraft({
approvalId,
action,
taskId,
reason,
sessionId: config.sessionId,
});
const stored = writeCommanderApproval(config, draft);
return { ok: true, approval: commanderApprovalPreview(stored), previewMarkdown: stored.previewMarkdown, previewJson: stored.previewJson };
}
export function handleCommanderRequest(req: Request): Promise<Response> | Response {
const url = new URL(req.url);
const path = routePath(url);
if (req.method === "GET" && (path === "/" || path === "/health")) return jsonResponse(commanderHealth(config, startedAt));
if (req.method === "GET" && path === "/logs") return jsonResponse({ ok: true, logs: recentLogs.slice(-100), logFile: config.logFile, stateRoot: config.stateRoot, startedAt });
if (req.method === "GET" && path === "/api/commander/contract") return jsonResponse(commanderContract());
if (req.method === "GET" && path === "/api/commander/sessions") return jsonResponse({ ok: true, sessions: listCommanderSessions(config).map(commanderSessionPreview) });
if (req.method === "GET" && path === "/api/commander/state") return jsonResponse({ ok: true, session: commanderSessionPreview(readCommanderSession(config, config.sessionId)), approval: commanderApprovalPreview(readCommanderApproval(config, "draft")) });
if (req.method === "POST" && path === "/api/commander/state") {
return readJsonBody(req).then((body) => jsonResponse({ ok: true, session: commanderSessionPreview(normalizeSessionUpdate(body)) }));
}
if (req.method === "GET" && path === "/api/commander/trace-summary") {
const traceJsonl = url.searchParams.get("traceJsonl") ?? "";
const taskSummary = url.searchParams.get("taskSummary");
const taskId = url.searchParams.get("taskId");
return jsonResponse({ ok: true, summary: summarizeCommanderTrace({ taskId, sessionId: config.sessionId, traceJsonl, taskSummary }) });
}
if (req.method === "POST" && path === "/api/commander/trace-summary") {
return readJsonBody(req).then((body) => jsonResponse(updateSessionFromTrace(body)));
}
if (req.method === "POST" && path === "/api/commander/approvals") {
return readJsonBody(req).then((body) => {
const result = writeApprovalDraft(body);
return jsonResponse(result, result.ok === false ? 400 : 200);
});
}
return jsonResponse({
ok: false,
service: config.serviceId,
status: "not_found",
available: ["/health", "/logs", "/api/commander/contract", "/api/commander/sessions", "/api/commander/state", "/api/commander/trace-summary", "/api/commander/approvals"],
}, 404);
}
if (import.meta.main && !isCheckMode) {
const server = Bun.serve({
hostname: config.host,
port: config.port,
idleTimeout: 120,
fetch(req) {
return Promise.resolve().then(() => handleCommanderRequest(req)).catch((error) => errorResponse(error));
},
});
log("service_started", { startedAt, service: config.serviceId, stateRoot: config.stateRoot, logFile: config.logFile, port: server.port });
}
@@ -0,0 +1,35 @@
const secretPatterns = [
/\b(?:sk|ghp|github_pat|xoxb|xoxp|AKIA)[A-Za-z0-9_=-]{8,}\b/gu,
/\b(?:token|secret|password|passwd|authorization|cookie|api[_-]?key)\s*[:=]\s*[^,\s]+/giu,
/\bBearer\s+[A-Za-z0-9._~+/-]+=*\b/giu,
/https?:\/\/[^/\s]+:[^@\s]+@[^/\s]+/giu,
] as const;
export interface RedactionResult {
text: string;
redactionsApplied: number;
}
export function redactText(value: string): RedactionResult {
let redactionsApplied = 0;
let text = value;
for (const pattern of secretPatterns) {
text = text.replace(pattern, () => {
redactionsApplied += 1;
return "<redacted>";
});
}
return { text, redactionsApplied };
}
export function redactJsonValue(value: unknown): unknown {
if (typeof value === "string") return redactText(value).text;
if (Array.isArray(value)) return value.map((item) => redactJsonValue(item));
if (typeof value !== "object" || value === null) return value;
const record = value as Record<string, unknown>;
const result: Record<string, unknown> = {};
for (const [key, item] of Object.entries(record)) {
result[key] = redactJsonValue(item);
}
return result;
}
@@ -0,0 +1,462 @@
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import type { CommanderApprovalState, CommanderPromptState, CommanderSessionState } from "./contract";
import { redactJsonValue, redactText } from "./redaction";
export interface CommanderStorageConfig {
rootDir: string;
}
export interface CommanderSessionRecord {
sessionId: string;
state: CommanderSessionState;
promptState: CommanderPromptState;
approvalState: CommanderApprovalState;
pid: number | null;
cwd: string | null;
lastSeq: number;
heartbeatAt: string | null;
updatedAt: string;
notes: string[];
}
export interface CommanderApprovalDraftRecord {
id: string;
action: string;
taskId: string | null;
reason: string;
status: CommanderApprovalState;
previewMarkdown: string;
previewJson: Record<string, unknown>;
redactionsApplied: number;
createdAt: string;
updatedAt: string;
}
export interface CommanderApprovalDraftInput {
approvalId?: string;
action: string;
taskId?: string | null;
reason: string;
sessionId?: string | null;
}
export interface CommanderTraceInput {
taskId: string | null;
sessionId: string;
traceJsonl: string;
taskSummary?: string | null;
}
export interface CommanderTraceSummary {
taskId: string | null;
sessionId: string;
lastSeq: number;
status: "running" | "attention_required" | "blocked" | "terminal" | "unknown";
keyEvents: string[];
openQuestions: string[];
recommendedNextActions: string[];
redactionsApplied: number;
sourceCount: number;
taskSummaryPreview: string | null;
traceLineCount: number;
}
export interface CommanderHealth {
ok: true;
service: "host-codex-commander";
status: "healthy";
startedAt: string;
stateRoot: string;
stateRootExists: boolean;
currentStateFile: string;
currentApprovalFile: string;
currentTraceFile: string;
currentLogFile: string;
}
interface PersistedSessionFile {
sessionId: string;
state: CommanderSessionState;
promptState: CommanderPromptState;
approvalState: CommanderApprovalState;
pid: number | null;
cwd: string | null;
lastSeq: number;
heartbeatAt: string | null;
updatedAt: string;
notes: string[];
}
interface PersistedApprovalFile {
id: string;
action: string;
taskId: string | null;
reason: string;
status: CommanderApprovalState;
previewMarkdown: string;
previewJson: Record<string, unknown>;
redactionsApplied: number;
createdAt: string;
updatedAt: string;
}
const defaultSessionId = "primary";
function ensureDir(path: string): void {
mkdirSync(path, { recursive: true });
}
function nowIso(): string {
return new Date().toISOString();
}
function safeJsonParse(text: string): Record<string, unknown> | null {
try {
const parsed = JSON.parse(text) as unknown;
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
return parsed as Record<string, unknown>;
} catch {
return null;
}
}
function clampText(value: string, maxChars = 1200): string {
const compact = value.replace(/\s+/gu, " ").trim();
return compact.length <= maxChars ? compact : `${compact.slice(0, Math.max(0, maxChars - 12))}…<truncated>`;
}
function stateFilePath(rootDir: string, sessionId = defaultSessionId): string {
return join(rootDir, "sessions", `${sessionId}.json`);
}
function traceFilePath(rootDir: string, sessionId = defaultSessionId): string {
return join(rootDir, "events", `${sessionId}.jsonl`);
}
function approvalFilePath(rootDir: string, approvalId = "draft"): string {
return join(rootDir, "approvals", `${approvalId}.json`);
}
function logFilePath(rootDir: string): string {
return join(rootDir, "logs", "commander.jsonl");
}
export function commanderStatePaths(config: CommanderStorageConfig, sessionId = defaultSessionId, approvalId = "draft"): {
stateFile: string;
approvalFile: string;
traceFile: string;
logFile: string;
} {
return {
stateFile: stateFilePath(config.rootDir, sessionId),
approvalFile: approvalFilePath(config.rootDir, approvalId),
traceFile: traceFilePath(config.rootDir, sessionId),
logFile: logFilePath(config.rootDir),
};
}
export function readCommanderSession(config: CommanderStorageConfig, sessionId = defaultSessionId): CommanderSessionRecord {
const path = stateFilePath(config.rootDir, sessionId);
if (!existsSync(path)) {
return {
sessionId,
state: "unknown",
promptState: "draft",
approvalState: "draft",
pid: null,
cwd: null,
lastSeq: 0,
heartbeatAt: null,
updatedAt: nowIso(),
notes: [],
};
}
const parsed = safeJsonParse(readFileSync(path, "utf8"));
if (parsed === null) throw new Error(`invalid commander session file: ${path}`);
return normalizeSessionRecord(parsed, sessionId);
}
export function writeCommanderSession(config: CommanderStorageConfig, session: CommanderSessionRecord): CommanderSessionRecord {
const path = stateFilePath(config.rootDir, session.sessionId);
ensureDir(dirname(path));
const normalized = normalizeSessionRecord(session, session.sessionId);
writeFileSync(path, `${JSON.stringify(normalized, null, 2)}\n`, "utf8");
return normalized;
}
export function readCommanderApproval(config: CommanderStorageConfig, approvalId = "draft"): CommanderApprovalDraftRecord {
const path = approvalFilePath(config.rootDir, approvalId);
if (!existsSync(path)) {
return {
id: approvalId,
action: "unknown",
taskId: null,
reason: "",
status: "draft",
previewMarkdown: "",
previewJson: { ok: true, status: "draft" },
redactionsApplied: 0,
createdAt: nowIso(),
updatedAt: nowIso(),
};
}
const parsed = safeJsonParse(readFileSync(path, "utf8"));
if (parsed === null) throw new Error(`invalid commander approval file: ${path}`);
return normalizeApprovalRecord(parsed, approvalId);
}
export function writeCommanderApproval(config: CommanderStorageConfig, approval: CommanderApprovalDraftRecord): CommanderApprovalDraftRecord {
const path = approvalFilePath(config.rootDir, approval.id);
ensureDir(dirname(path));
const normalized = normalizeApprovalRecord(approval, approval.id);
writeFileSync(path, `${JSON.stringify(normalized, null, 2)}\n`, "utf8");
return normalized;
}
export function buildCommanderApprovalDraft(input: CommanderApprovalDraftInput): CommanderApprovalDraftRecord {
const approvalId = input.approvalId ?? "draft";
const reason = redactText(input.reason);
const action = String(input.action || "unknown");
const taskId = input.taskId ?? null;
const previewMarkdown = [
"# Commander approval draft",
"",
`- approvalId: ${approvalId}`,
`- action: ${action}`,
`- taskId: ${taskId ?? "null"}`,
`- status: draft`,
`- redactionsApplied: ${reason.redactionsApplied}`,
input.sessionId ? `- sessionId: ${input.sessionId}` : "- sessionId: null",
"",
"## Reason",
"",
reason.text || "(empty)",
"",
"## Boundary",
"",
"- ClaudeQQ send is not implemented in this skeleton.",
"- Approval is preview-only until explicit user approval is recorded.",
].join("\n");
return {
id: approvalId,
action,
taskId,
reason: reason.text,
status: "draft",
previewMarkdown,
previewJson: redactJsonValue({
ok: true,
service: "host-codex-commander",
approvalId,
action,
taskId,
reason: reason.text,
redactionsApplied: reason.redactionsApplied,
requiresExplicitUserApproval: true,
sendImplemented: false,
claudeqq: {
mutation: false,
target: "preview-only",
messageTemplate: `Approval required for ${action}. Reason: ${reason.text}.`,
},
blockedUntilApproved: [action],
}) as Record<string, unknown>,
redactionsApplied: reason.redactionsApplied,
createdAt: nowIso(),
updatedAt: nowIso(),
};
}
export function commanderSessionPreview(session: CommanderSessionRecord): Record<string, unknown> {
return redactJsonValue({
sessionId: session.sessionId,
state: session.state,
promptState: session.promptState,
approvalState: session.approvalState,
pid: session.pid,
cwd: session.cwd,
lastSeq: session.lastSeq,
heartbeatAt: session.heartbeatAt,
updatedAt: session.updatedAt,
notes: session.notes,
}) as Record<string, unknown>;
}
export function commanderApprovalPreview(approval: CommanderApprovalDraftRecord): Record<string, unknown> {
return redactJsonValue({
id: approval.id,
action: approval.action,
taskId: approval.taskId,
reason: approval.reason,
status: approval.status,
redactionsApplied: approval.redactionsApplied,
createdAt: approval.createdAt,
updatedAt: approval.updatedAt,
}) as Record<string, unknown>;
}
export function listCommanderSessions(config: CommanderStorageConfig): CommanderSessionRecord[] {
const dir = join(config.rootDir, "sessions");
if (!existsSync(dir)) return [readCommanderSession(config)];
const sessions = readdirSync(dir)
.filter((name) => name.endsWith(".json"))
.map((name) => name.slice(0, -".json".length))
.map((sessionId) => {
try {
return readCommanderSession(config, sessionId);
} catch {
return null;
}
})
.filter((item): item is CommanderSessionRecord => item !== null);
if (sessions.length === 0) sessions.push(readCommanderSession(config));
return sessions.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
}
export function appendCommanderTraceEvent(config: CommanderStorageConfig, sessionId: string, event: Record<string, unknown>): string {
const path = traceFilePath(config.rootDir, sessionId);
ensureDir(dirname(path));
const redactedEvent = redactJsonValue({ ...event, at: nowIso(), sessionId });
writeFileSync(path, `${JSON.stringify(redactedEvent)}\n`, { flag: "a" });
return path;
}
export function summarizeCommanderTrace(input: CommanderTraceInput): CommanderTraceSummary {
const lines = input.traceJsonl.split(/\r?\n/gu).map((line) => line.trim()).filter(Boolean);
const keyEvents: string[] = [];
const openQuestions: string[] = [];
const recommendedNextActions: string[] = [];
let lastSeq = 0;
let status: CommanderTraceSummary["status"] = "unknown";
let redactionsApplied = 0;
for (const line of lines) {
const parsed = safeJsonParse(line);
if (parsed === null) continue;
const seq = Number(parsed.seq ?? parsed.sequence ?? parsed.eventSequence ?? 0);
if (Number.isFinite(seq)) lastSeq = Math.max(lastSeq, Math.floor(seq));
const rawText = String(parsed.summary ?? parsed.text ?? parsed.message ?? parsed.command ?? parsed.output ?? "");
const redacted = redactText(rawText);
redactionsApplied += redacted.redactionsApplied;
const compact = clampText(redacted.text, 180);
const kind = String(parsed.kind ?? parsed.channel ?? parsed.type ?? "event");
const statusText = String(parsed.status ?? "").toLowerCase();
if (statusText.includes("failed") || statusText.includes("error")) status = "blocked";
else if (statusText.includes("completed") || statusText.includes("succeeded")) status = "terminal";
else if (statusText.includes("blocked") || statusText.includes("attention")) status = "attention_required";
else if (status === "unknown") status = "running";
if (compact.length > 0 && keyEvents.length < 8) keyEvents.push(`${kind}: ${compact}`);
}
const taskSummary = input.taskSummary ? redactText(input.taskSummary) : null;
if (taskSummary !== null) redactionsApplied += taskSummary.redactionsApplied;
const summaryPreview = taskSummary === null ? null : clampText(taskSummary.text, 300);
if (status === "unknown" && lines.length > 0) status = "running";
if (status === "running") {
openQuestions.push("Confirm whether current host Codex session still needs direct control.");
recommendedNextActions.push("Review the last session events and keep the state file current.");
} else if (status === "attention_required") {
openQuestions.push("Identify the action that needs approval or operator intervention.");
recommendedNextActions.push("Draft approval text before any live command path is considered.");
} else if (status === "blocked") {
openQuestions.push("Identify the blocking error or missing prerequisite.");
recommendedNextActions.push("Capture the failure summary and keep the action non-live.");
} else if (status === "terminal") {
recommendedNextActions.push("Persist the terminal summary and mark the session read.");
} else {
recommendedNextActions.push("Collect more trace lines before making a state claim.");
}
return {
taskId: input.taskId,
sessionId: input.sessionId,
lastSeq,
status,
keyEvents,
openQuestions,
recommendedNextActions,
redactionsApplied,
sourceCount: lines.length,
taskSummaryPreview: summaryPreview,
traceLineCount: lines.length,
};
}
export function commanderHealth(config: CommanderStorageConfig, startedAt: string): CommanderHealth {
const paths = commanderStatePaths(config);
return {
ok: true,
service: "host-codex-commander",
status: "healthy",
startedAt,
stateRoot: config.rootDir,
stateRootExists: existsSync(config.rootDir),
currentStateFile: paths.stateFile,
currentApprovalFile: paths.approvalFile,
currentTraceFile: paths.traceFile,
currentLogFile: paths.logFile,
};
}
function normalizeSessionRecord(record: Record<string, unknown>, sessionId: string): CommanderSessionRecord {
const notes = Array.isArray(record.notes) ? record.notes.filter((item): item is string => typeof item === "string").map((item) => redactText(item).text) : [];
return {
sessionId: String(record.sessionId ?? sessionId),
state: normalizeSessionState(String(record.state ?? "unknown")),
promptState: normalizePromptState(String(record.promptState ?? "draft")),
approvalState: normalizeApprovalState(String(record.approvalState ?? "draft")),
pid: typeof record.pid === "number" && Number.isFinite(record.pid) ? Math.floor(record.pid) : null,
cwd: typeof record.cwd === "string" && record.cwd.length > 0 ? record.cwd : null,
lastSeq: Number.isFinite(Number(record.lastSeq ?? 0)) ? Math.max(0, Math.floor(Number(record.lastSeq ?? 0))) : 0,
heartbeatAt: typeof record.heartbeatAt === "string" && record.heartbeatAt.length > 0 ? record.heartbeatAt : null,
updatedAt: typeof record.updatedAt === "string" && record.updatedAt.length > 0 ? record.updatedAt : nowIso(),
notes,
};
}
function normalizeApprovalRecord(record: Record<string, unknown>, approvalId: string): CommanderApprovalDraftRecord {
const previewMarkdown = typeof record.previewMarkdown === "string" ? redactText(record.previewMarkdown).text : "";
const previewJson = typeof record.previewJson === "object" && record.previewJson !== null && !Array.isArray(record.previewJson)
? redactJsonValue(record.previewJson) as Record<string, unknown>
: { ok: true, status: "draft" };
return {
id: String(record.id ?? approvalId),
action: typeof record.action === "string" ? record.action : "unknown",
taskId: typeof record.taskId === "string" ? record.taskId : null,
reason: typeof record.reason === "string" ? redactText(record.reason).text : "",
status: normalizeApprovalState(String(record.status ?? "draft")),
previewMarkdown,
previewJson,
redactionsApplied: Number.isFinite(Number(record.redactionsApplied ?? 0)) ? Math.max(0, Math.floor(Number(record.redactionsApplied ?? 0))) : 0,
createdAt: typeof record.createdAt === "string" && record.createdAt.length > 0 ? record.createdAt : nowIso(),
updatedAt: typeof record.updatedAt === "string" && record.updatedAt.length > 0 ? record.updatedAt : nowIso(),
};
}
function normalizeSessionState(value: string): CommanderSessionState {
return ["unknown", "discovered", "planned", "starting", "running", "attention_required", "stopping", "stopped", "degraded"].includes(value)
? value as CommanderSessionState
: "unknown";
}
function normalizePromptState(value: string): CommanderPromptState {
return ["draft", "planned", "queued_for_injection", "injected", "rejected", "failed"].includes(value)
? value as CommanderPromptState
: "draft";
}
function normalizeApprovalState(value: string): CommanderApprovalState {
return ["draft", "requested", "approved", "rejected", "expired", "consumed"].includes(value)
? value as CommanderApprovalState
: "draft";
}
export function normalizeCommanderSessionState(value: string): CommanderSessionState {
return normalizeSessionState(value);
}
export function normalizeCommanderPromptState(value: string): CommanderPromptState {
return normalizePromptState(value);
}
export function normalizeCommanderApprovalState(value: string): CommanderApprovalState {
return normalizeApprovalState(value);
}
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"composite": true,
"rootDir": "../../",
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["bun", "node"],
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "dist",
"skipLibCheck": true
},
"include": ["src/**/*.ts", "../../shared/src/**/*.ts"]
}