fix(runtime): deep audit fixes — clarification loop termination + callback alignment
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

CRITICAL:
- ask_clarification now terminates Agent Loop in both run() and run_streaming()
  paths, preventing the LLM from continuing after requesting user clarification

HIGH:
- SaaS relay now forwards plan_mode and subagent_enabled to backend
- GatewayClient.chatStream now supports onThinkingDelta, onSubtaskStatus,
  and token-bearing onComplete — aligned with kernel-types StreamCallbacks
- ZclawStreamEvent type extended with thinking_delta, subtask_status variants
  and input_tokens/output_tokens fields for token tracking via Gateway path
This commit is contained in:
iven
2026-04-06 16:50:48 +08:00
parent 9339b64bae
commit 6a13fff9ec
4 changed files with 99 additions and 5 deletions

View File

@@ -175,9 +175,11 @@ export class GatewayClient {
private deviceKeysPromise: Promise<DeviceKeys>;
private streamCallbacks = new Map<string, {
onDelta: (delta: string) => void;
onThinkingDelta?: (delta: string) => void;
onTool?: (tool: string, input: string, output: string) => void;
onHand?: (name: string, status: string, result?: unknown) => void;
onComplete: () => void;
onSubtaskStatus?: (description: string, status: string, detail?: string) => void;
onComplete: (inputTokens?: number, outputTokens?: number) => void;
onError: (error: string) => void;
}>();
@@ -465,9 +467,11 @@ export class GatewayClient {
message: string,
callbacks: {
onDelta: (delta: string) => void;
onThinkingDelta?: (delta: string) => void;
onTool?: (tool: string, input: string, output: string) => void;
onHand?: (name: string, status: string, result?: unknown) => void;
onComplete: () => void;
onSubtaskStatus?: (description: string, status: string, detail?: string) => void;
onComplete: (inputTokens?: number, outputTokens?: number) => void;
onError: (error: string) => void;
},
opts?: {
@@ -638,10 +642,26 @@ export class GatewayClient {
}
break;
case 'thinking_delta':
// Extended thinking delta
if (data.content && callbacks.onThinkingDelta) {
callbacks.onThinkingDelta(data.content);
}
break;
case 'subtask_status':
// Sub-agent task status update
if (callbacks.onSubtaskStatus && data.description) {
callbacks.onSubtaskStatus(data.description, data.status || '', data.detail);
}
break;
case 'phase':
// Phase change: streaming | done
if (data.phase === 'done') {
callbacks.onComplete();
const inputTokens = typeof data.input_tokens === 'number' ? data.input_tokens : undefined;
const outputTokens = typeof data.output_tokens === 'number' ? data.output_tokens : undefined;
callbacks.onComplete(inputTokens, outputTokens);
this.streamCallbacks.delete(runId);
if (this.zclawWs) {
this.zclawWs.close(1000, 'Stream complete');
@@ -657,7 +677,11 @@ export class GatewayClient {
callbacks.onDelta(data.content);
}
// Mark complete if phase done wasn't sent
callbacks.onComplete();
{
const inputTokens = typeof data.input_tokens === 'number' ? data.input_tokens : undefined;
const outputTokens = typeof data.output_tokens === 'number' ? data.output_tokens : undefined;
callbacks.onComplete(inputTokens, outputTokens);
}
this.streamCallbacks.delete(runId);
if (this.zclawWs) {
this.zclawWs.close(1000, 'Stream complete');