| 1 |
/** |
| 2 |
* clientTerminalSnapshot — synthesizes a canonical TurnState on the client |
| 3 |
* for transport-layer failures the brain never observed. |
| 4 |
* |
| 5 |
* Why this exists (Phase 4 polish): |
| 6 |
* Pre-brain failures (DNS, CORS, Laravel 5xx, SSE abort, brain queue |
| 7 |
* unavailable) used to surface as ad-hoc strings: a red `network error` |
| 8 |
* banner bound to the free-form Axios `error.message`, plus a hardcoded |
| 9 |
* system-role bubble `"Sorry, I encountered an error. Please try again."`. |
| 10 |
* Neither path touched turnSnapshot, which re-introduced the same |
| 11 |
* silence-as-success / prose-as-truth class this contract was built to |
| 12 |
* eliminate. |
| 13 |
* |
| 14 |
* Contract: |
| 15 |
* - The shape MUST match workers/agent-brain/src/types/contract.ts |
| 16 |
* (TurnStateSchema). If brain-side Zod rejects this shape at the |
| 17 |
* Laravel/React boundary in future, add a matching client synth here. |
| 18 |
* - `status` collapses to 'unresolved' for transport_error (we don't |
| 19 |
* know what happened server-side) and 'aborted' for client_abort |
| 20 |
* (user intent). |
| 21 |
* - `tool_calls` is always [] — the client has no authority to fabricate |
| 22 |
* tool execution history. |
| 23 |
* - `turn_id` is prefixed with `client_` so snapshots from this path are |
| 24 |
* trivially distinguishable from brain-authored ones during triage. |
| 25 |
*/ |
| 26 |
|
| 27 |
export const TURN_STATE_SCHEMA_VERSION = 1; |
| 28 |
|
| 29 |
/** |
| 30 |
* @param {Object} params |
| 31 |
* @param {'transport_error' | 'client_abort'} params.reason |
| 32 |
* @param {string | null} params.sessionId |
| 33 |
* @param {string | null} [params.turnId] - if the transport failure happened |
| 34 |
* after Laravel assigned a turn_id, |
| 35 |
* pass it through so reconciliation |
| 36 |
* can align. |
| 37 |
* @param {string | null} [params.detail] - raw error message, stored as the |
| 38 |
* terminal.last_tool_error.message |
| 39 |
* with a sentinel tool name for |
| 40 |
* devtools/triage visibility. |
| 41 |
*/ |
| 42 |
export function buildClientTerminalSnapshot({ reason, sessionId, turnId = null, detail = null }) { |
| 43 |
const now = new Date().toISOString(); |
| 44 |
const status = reason === 'client_abort' ? 'aborted' : 'unresolved'; |
| 45 |
|
| 46 |
return { |
| 47 |
schema_version: TURN_STATE_SCHEMA_VERSION, |
| 48 |
turn_id: turnId || `client_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`, |
| 49 |
session_id: sessionId || '', |
| 50 |
started_at: now, |
| 51 |
ended_at: now, |
| 52 |
status, |
| 53 |
stage: 'unknown', |
| 54 |
terminal: { |
| 55 |
reason, |
| 56 |
failure_class: null, |
| 57 |
tool_error_count: 0, |
| 58 |
unresolved_todo_count: 0, |
| 59 |
last_stage: 'unknown', |
| 60 |
last_tool_error: detail |
| 61 |
? { tool: '__transport__', call_id: 'client', message: String(detail) } |
| 62 |
: null, |
| 63 |
}, |
| 64 |
plan_state: null, |
| 65 |
active_todos: [], |
| 66 |
tool_calls: [], |
| 67 |
}; |
| 68 |
} |
| 69 |
|