@yeaft/webchat-agent 1.0.289 → 1.0.292
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +94 -90
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/yeaft/cli-session-runner.js +213 -0
- package/yeaft/cli.js +75 -13
- package/yeaft/engine.js +64 -17
- package/yeaft/memory/preflow.js +8 -2
- package/yeaft/sessions/pre-flow.js +5 -0
- package/yeaft/stdio-protocol.js +164 -0
- package/yeaft/web-bridge.js +1 -0
package/yeaft/stdio-protocol.js
CHANGED
|
@@ -297,3 +297,167 @@ export async function runStreamTurn({
|
|
|
297
297
|
write(result);
|
|
298
298
|
return result;
|
|
299
299
|
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Run one prompt through the multi-VP CLI Session runner. Engine events keep
|
|
303
|
+
* the stream-json shape used by runStreamTurn and add `vp_id`, allowing
|
|
304
|
+
* concurrent VP streams to be demultiplexed without imposing output order.
|
|
305
|
+
*/
|
|
306
|
+
export async function runStreamSessionTurn({
|
|
307
|
+
runner,
|
|
308
|
+
prompt,
|
|
309
|
+
sessionId,
|
|
310
|
+
workDir = process.cwd(),
|
|
311
|
+
model = null,
|
|
312
|
+
modelEffort = null,
|
|
313
|
+
input = null,
|
|
314
|
+
write,
|
|
315
|
+
}) {
|
|
316
|
+
const clientTurnId = randomUUID();
|
|
317
|
+
const states = new Map();
|
|
318
|
+
let failed = null;
|
|
319
|
+
|
|
320
|
+
const stateFor = (vpId) => {
|
|
321
|
+
let state = states.get(vpId);
|
|
322
|
+
if (!state) {
|
|
323
|
+
state = {
|
|
324
|
+
turnId: clientTurnId,
|
|
325
|
+
resultText: '',
|
|
326
|
+
stopReason: 'end_turn',
|
|
327
|
+
usage: { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, cacheInputDeltaTokens: 0 },
|
|
328
|
+
};
|
|
329
|
+
states.set(vpId, state);
|
|
330
|
+
}
|
|
331
|
+
return state;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const askUser = async ({ question, options }, vpId = null, turnId = clientTurnId) => {
|
|
335
|
+
const requestId = randomUUID();
|
|
336
|
+
write({
|
|
337
|
+
type: 'ask_user', subtype: 'request', request_id: requestId,
|
|
338
|
+
session_id: sessionId, turn_id: turnId, vp_id: vpId,
|
|
339
|
+
question, options: Array.isArray(options) ? options : [],
|
|
340
|
+
});
|
|
341
|
+
if (!input) throw new Error('AskUser requires --input-format stream-json');
|
|
342
|
+
const answers = await input.waitForAnswer(requestId);
|
|
343
|
+
write({
|
|
344
|
+
type: 'ask_user', subtype: 'response', request_id: requestId,
|
|
345
|
+
session_id: sessionId, turn_id: turnId, vp_id: vpId, answers,
|
|
346
|
+
});
|
|
347
|
+
return answers;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const onEvent = async ({ vpId, event, turnId: fallbackTurnId }) => {
|
|
351
|
+
const state = stateFor(vpId);
|
|
352
|
+
if (event.type === 'turn_open' && event.turnId) state.turnId = event.turnId;
|
|
353
|
+
const turnId = state.turnId || fallbackTurnId || clientTurnId;
|
|
354
|
+
const base = { session_id: sessionId, turn_id: turnId, vp_id: vpId };
|
|
355
|
+
switch (event.type) {
|
|
356
|
+
case 'turn_open':
|
|
357
|
+
write({ ...event, type: 'turn', subtype: 'start', ...base, model, model_effort: modelEffort, cwd: workDir });
|
|
358
|
+
break;
|
|
359
|
+
case 'text_delta':
|
|
360
|
+
state.resultText += event.text || '';
|
|
361
|
+
write({ type: 'assistant', subtype: 'text_delta', ...base, delta: { type: 'text_delta', text: event.text || '' } });
|
|
362
|
+
break;
|
|
363
|
+
case 'thinking_delta':
|
|
364
|
+
write({ type: 'assistant', subtype: 'thinking_delta', ...base, delta: { type: 'thinking_delta', thinking: event.text || '' } });
|
|
365
|
+
break;
|
|
366
|
+
case 'skill_loaded':
|
|
367
|
+
write({ type: 'skill', subtype: 'loaded', ...base, skill: event.skill });
|
|
368
|
+
break;
|
|
369
|
+
case 'skill_error':
|
|
370
|
+
write({ type: 'skill', subtype: 'error', ...base, skill_name: event.skillName, error: event.message });
|
|
371
|
+
break;
|
|
372
|
+
case 'tool_call':
|
|
373
|
+
write({ type: 'assistant', subtype: 'tool_use', ...base, content: [{ type: 'tool_use', id: event.id, name: event.name, input: event.input }] });
|
|
374
|
+
if (event.name === 'TodoWrite') {
|
|
375
|
+
write({ type: 'todo', subtype: 'update', ...base, tool_use_id: event.id, todos: event.input?.todos || [] });
|
|
376
|
+
}
|
|
377
|
+
break;
|
|
378
|
+
case 'tool_start':
|
|
379
|
+
write({ type: 'tool', subtype: 'start', ...base, tool_use_id: event.id, name: event.name, input: event.input });
|
|
380
|
+
break;
|
|
381
|
+
case 'tool_end':
|
|
382
|
+
write({ type: 'tool', subtype: 'result', ...base, tool_use_id: event.id, name: event.name, content: event.output, is_error: !!event.isError });
|
|
383
|
+
break;
|
|
384
|
+
case 'usage': {
|
|
385
|
+
const cacheDelta = event.cacheTokensAreIncludedInInput ? 0 : (event.cacheReadTokens || 0) + (event.cacheWriteTokens || 0);
|
|
386
|
+
state.usage.inputTokens += event.inputTokens || 0;
|
|
387
|
+
state.usage.outputTokens += event.outputTokens || 0;
|
|
388
|
+
state.usage.cacheReadTokens += event.cacheReadTokens || 0;
|
|
389
|
+
state.usage.cacheWriteTokens += event.cacheWriteTokens || 0;
|
|
390
|
+
state.usage.cacheInputDeltaTokens += cacheDelta;
|
|
391
|
+
write({ type: 'usage', ...base, usage: snakeUsage(event) });
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
case 'stop':
|
|
395
|
+
case 'turn_end':
|
|
396
|
+
if (event.stopReason && TERMINAL_STOP_REASONS.has(event.stopReason)) state.stopReason = event.stopReason;
|
|
397
|
+
break;
|
|
398
|
+
case 'turn_close':
|
|
399
|
+
write({ type: 'turn', subtype: 'stop', ...base, duration_ms: event.totalMs, loop_count: event.loopCount, total_tokens: event.totalTokens });
|
|
400
|
+
break;
|
|
401
|
+
case 'error':
|
|
402
|
+
failed ||= event.error instanceof Error ? event.error : new Error(event.error?.message || String(event.error || 'Unknown error'));
|
|
403
|
+
write({ type: 'error', ...base, error: { name: failed.name, message: failed.message }, retryable: !!event.retryable });
|
|
404
|
+
break;
|
|
405
|
+
case 'fallback':
|
|
406
|
+
case 'llm_retry':
|
|
407
|
+
case 'memory_used':
|
|
408
|
+
case 'recall':
|
|
409
|
+
case 'consolidate':
|
|
410
|
+
case 'reflection':
|
|
411
|
+
write({ ...event, ...base });
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
let outcome;
|
|
417
|
+
try {
|
|
418
|
+
outcome = await runner.run(prompt, {
|
|
419
|
+
modelEffort,
|
|
420
|
+
onEvent,
|
|
421
|
+
askUser,
|
|
422
|
+
});
|
|
423
|
+
} catch (error) {
|
|
424
|
+
failed = error;
|
|
425
|
+
write({ type: 'error', session_id: sessionId, turn_id: clientTurnId, error: { name: error.name || 'Error', message: error.message || String(error) } });
|
|
426
|
+
outcome = { report: { dispatched: [] }, results: [] };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const perVp = Array.from(states, ([vpId, state]) => ({
|
|
430
|
+
vp_id: vpId,
|
|
431
|
+
turn_id: state.turnId,
|
|
432
|
+
stop_reason: state.stopReason,
|
|
433
|
+
result: state.resultText,
|
|
434
|
+
usage: {
|
|
435
|
+
input_tokens: state.usage.inputTokens,
|
|
436
|
+
output_tokens: state.usage.outputTokens,
|
|
437
|
+
cache_read_input_tokens: state.usage.cacheReadTokens,
|
|
438
|
+
cache_creation_input_tokens: state.usage.cacheWriteTokens,
|
|
439
|
+
total_input_tokens: state.usage.inputTokens + state.usage.cacheInputDeltaTokens,
|
|
440
|
+
total_tokens: state.usage.inputTokens + state.usage.cacheInputDeltaTokens + state.usage.outputTokens,
|
|
441
|
+
},
|
|
442
|
+
}));
|
|
443
|
+
const stopReasons = perVp.map(item => item.stop_reason);
|
|
444
|
+
const aggregateStopReason = failed || stopReasons.includes('error')
|
|
445
|
+
? 'error'
|
|
446
|
+
: stopReasons.length > 0 && stopReasons.every(reason => reason === 'aborted')
|
|
447
|
+
? 'aborted'
|
|
448
|
+
: 'end_turn';
|
|
449
|
+
const isError = !!failed || aggregateStopReason === 'error';
|
|
450
|
+
const result = {
|
|
451
|
+
type: 'result', subtype: isError ? 'error' : 'success',
|
|
452
|
+
session_id: sessionId, turn_id: clientTurnId,
|
|
453
|
+
model, model_effort: modelEffort,
|
|
454
|
+
stop_reason: aggregateStopReason,
|
|
455
|
+
is_error: isError,
|
|
456
|
+
result: perVp.map(item => item.result).filter(Boolean).join('\n'),
|
|
457
|
+
dispatched_vp_ids: outcome?.report?.dispatched || [],
|
|
458
|
+
vp_results: perVp,
|
|
459
|
+
...(failed ? { error: failed.message || String(failed) } : {}),
|
|
460
|
+
};
|
|
461
|
+
write(result);
|
|
462
|
+
return result;
|
|
463
|
+
}
|