heartbeat added
This commit is contained in:
@@ -140,18 +140,35 @@ Please follow your instructions to produce the funding outlook brief.`
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----- Pipe agent activity to the client -----
|
// ----- Pipe agent activity to the client -----
|
||||||
// The agent emits multiple event types. We surface:
|
// Long stretches between agent.message events (model "thinking",
|
||||||
// - agent.message text blocks (narration)
|
// multi-tool batches, etc.) can silence the response stream long enough
|
||||||
// - agent.tool_use for the `write` tool, whose `input.content` IS the
|
// that Netlify's edge proxy drops the connection. To keep bytes flowing
|
||||||
// final brief the agent produces (it writes it as a Markdown file in
|
// we (a) forward extra event types as concise status lines and (b) emit
|
||||||
// the sandbox instead of streaming it back)
|
// a periodic heartbeat space when the upstream goes quiet.
|
||||||
// - lightweight status lines for other tool calls so the UI keeps moving
|
|
||||||
const encoder = new TextEncoder()
|
const encoder = new TextEncoder()
|
||||||
const seenEventIds = new Set()
|
const seenEventIds = new Set()
|
||||||
const writtenFiles = new Set() // dedupe writes by path
|
const writtenFiles = new Set() // dedupe writes by path
|
||||||
|
const HEARTBEAT_MS = 10_000
|
||||||
|
|
||||||
const stream = new ReadableStream({
|
const stream = new ReadableStream({
|
||||||
async start(controller) {
|
async start(controller) {
|
||||||
const send = (s) => controller.enqueue(encoder.encode(s))
|
let lastSendAt = Date.now()
|
||||||
|
const send = (s) => {
|
||||||
|
controller.enqueue(encoder.encode(s))
|
||||||
|
lastSendAt = Date.now()
|
||||||
|
}
|
||||||
|
// Zero-width space — invisible in rendered Markdown but counts as a
|
||||||
|
// byte on the wire, which is enough to defeat proxy idle-timeouts.
|
||||||
|
const heartbeat = setInterval(() => {
|
||||||
|
if (Date.now() - lastSendAt >= HEARTBEAT_MS) {
|
||||||
|
try {
|
||||||
|
controller.enqueue(encoder.encode('\u200B'))
|
||||||
|
lastSendAt = Date.now()
|
||||||
|
} catch {
|
||||||
|
/* controller may have closed */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, HEARTBEAT_MS / 2)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for await (const event of upstream) {
|
for await (const event of upstream) {
|
||||||
@@ -170,6 +187,11 @@ Please follow your instructions to produce the funding outlook brief.`
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'agent.thinking': {
|
||||||
|
send(`\n\n_💭 thinking…_\n\n`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
case 'agent.tool_use': {
|
case 'agent.tool_use': {
|
||||||
// The write tool carries the actual brief in input.content.
|
// The write tool carries the actual brief in input.content.
|
||||||
if (
|
if (
|
||||||
@@ -194,6 +216,20 @@ Please follow your instructions to produce the funding outlook brief.`
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'agent.tool_result':
|
||||||
|
case 'agent.mcp_tool_result': {
|
||||||
|
if (event.is_error) {
|
||||||
|
const msg =
|
||||||
|
(Array.isArray(event.content) &&
|
||||||
|
event.content[0]?.text) ||
|
||||||
|
'tool error'
|
||||||
|
send(`\n\n_⚠️ tool error: ${String(msg).slice(0, 200)}_\n\n`)
|
||||||
|
} else {
|
||||||
|
send(`\n\n_✓ result_\n\n`)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
case 'session.error': {
|
case 'session.error': {
|
||||||
const msg = event.error?.message || 'unknown session error'
|
const msg = event.error?.message || 'unknown session error'
|
||||||
send(`\n\n[session error: ${msg}]`)
|
send(`\n\n[session error: ${msg}]`)
|
||||||
@@ -211,8 +247,10 @@ Please follow your instructions to produce the funding outlook brief.`
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clearInterval(heartbeat)
|
||||||
controller.close()
|
controller.close()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
clearInterval(heartbeat)
|
||||||
send(`\n\n[stream error: ${err.message || err}]`)
|
send(`\n\n[stream error: ${err.message || err}]`)
|
||||||
controller.close()
|
controller.close()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user