Move all hand-synced cross-app wire contracts into one built workspace package, @boocode/contracts, consumed by server/web/coder/coder-web via workspace:* + a per-subpath exports map. The ws-frames and provider-config Zod schemas are schema-first (z.infer); MessageMetadata, ErrorReason, AgentSessionConfig, the provider snapshot types, and WorktreeRiskReport are each single-sourced. Deletes the byte-identical copies and their parity tests, fixes a live AgentSessionConfig drift (coder dead copy removed, unified to the web required/nullable shape), removes the dead pending_change WS arms in the fallback SPA, and inverts the build order (contracts builds first) across root build, Dockerfile, and the coder deploy docs. Reverses the shared-package decision declined in v2.5.12. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
56 lines
2.2 KiB
TypeScript
56 lines
2.2 KiB
TypeScript
/**
|
|
* agent-status-publish (#10) — builds + publishes the `agent_status_updated`
|
|
* WS frame on the per-session channel (the same channel CoderPane subscribes to).
|
|
*
|
|
* Kept separate from normalize-agent-status.ts so that module stays a pure,
|
|
* broker-free helper (trivially unit-testable; reused by the config-injection
|
|
* follow-on). The frame contract is pinned in apps/server/src/types/ws-frames.ts
|
|
* (`AgentStatusUpdatedFrame`) and mirrored byte-identical in apps/web.
|
|
*/
|
|
import type { Broker } from '@boocode/server/broker';
|
|
import type { WsFrame } from '@boocode/contracts/ws-frames';
|
|
import type { AgentStatus } from './normalize-agent-status.js';
|
|
|
|
// The exact slice of Broker we need — accepting just the bound method keeps call
|
|
// sites flexible (pass `broker.publishFrame.bind(broker)` or, since the broker's
|
|
// publishFrame doesn't read `this`, `broker.publishFrame` directly).
|
|
type PublishFrame = Broker['publishFrame'];
|
|
|
|
/**
|
|
* Best-effort publish of a normalized agent status. The broker's publishFrame
|
|
* already fail-closes (validates + logs + drops on bad input, never throws), but
|
|
* we additionally swallow any unexpected error so a publish can NEVER break the
|
|
* turn it's reporting on.
|
|
*
|
|
* @param publishFrame the session channel publisher (broker.publishFrame)
|
|
* @param sessionId WS subscription channel (CoderPane subscribes per-session)
|
|
* @param chatId the (chat) half of the (chat, agent) status key
|
|
* @param agent the (agent) half of the key
|
|
* @param status normalized lifecycle status
|
|
* @param reason free-form discriminator (turn_start / turn_complete / …)
|
|
* @param at ISO timestamp; defaults to now
|
|
*/
|
|
export function publishAgentStatus(
|
|
publishFrame: PublishFrame,
|
|
sessionId: string,
|
|
chatId: string,
|
|
agent: string,
|
|
status: AgentStatus,
|
|
reason?: string,
|
|
at: string = new Date().toISOString(),
|
|
): void {
|
|
try {
|
|
const frame: WsFrame = {
|
|
type: 'agent_status_updated',
|
|
chat_id: chatId,
|
|
agent,
|
|
status,
|
|
...(reason ? { reason } : {}),
|
|
at,
|
|
};
|
|
publishFrame(sessionId, frame);
|
|
} catch {
|
|
// never let a status publish break the turn — best-effort only.
|
|
}
|
|
}
|