@poncho-ai/cli 0.33.1 → 0.33.3
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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +15 -0
- package/dist/{chunk-IDGGF5WH.js → chunk-METMUDY6.js} +496 -817
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/{run-interactive-ink-R4PHKIQR.js → run-interactive-ink-XQM7OIZT.js} +1 -1
- package/package.json +3 -3
- package/src/index.ts +599 -930
- package/src/init-onboarding.ts +2 -7
package/src/index.ts
CHANGED
|
@@ -303,15 +303,21 @@ const parseParams = (values: string[]): Record<string, string> => {
|
|
|
303
303
|
return params;
|
|
304
304
|
};
|
|
305
305
|
|
|
306
|
-
const normalizeMessageForClient = (message: Message): Message => {
|
|
306
|
+
const normalizeMessageForClient = (message: Message): Message | null => {
|
|
307
|
+
// Hide tool-role and system-role messages from the web UI — they are
|
|
308
|
+
// internal harness bookkeeping that leaks into conv.messages when
|
|
309
|
+
// _harnessMessages are used as canonical history.
|
|
310
|
+
if (message.role === "tool" || message.role === "system") {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
307
313
|
if (message.role !== "assistant" || typeof message.content !== "string") {
|
|
308
314
|
return message;
|
|
309
315
|
}
|
|
310
316
|
try {
|
|
311
317
|
const parsed = JSON.parse(message.content) as Record<string, unknown>;
|
|
312
|
-
const text = typeof parsed.text === "string" ? parsed.text : undefined;
|
|
313
318
|
const toolCalls = Array.isArray(parsed.tool_calls) ? parsed.tool_calls : undefined;
|
|
314
|
-
if (
|
|
319
|
+
if (toolCalls) {
|
|
320
|
+
const text = typeof parsed.text === "string" ? parsed.text : "";
|
|
315
321
|
const meta = { ...(message.metadata ?? {}) } as Record<string, unknown>;
|
|
316
322
|
if (!meta.sections && toolCalls.length > 0) {
|
|
317
323
|
const toolLabels = toolCalls.map((tc: Record<string, unknown>) => {
|
|
@@ -491,12 +497,14 @@ const buildAssistantMetadata = (
|
|
|
491
497
|
const executeConversationTurn = async ({
|
|
492
498
|
harness,
|
|
493
499
|
runInput,
|
|
500
|
+
events,
|
|
494
501
|
initialContextTokens = 0,
|
|
495
502
|
initialContextWindow = 0,
|
|
496
503
|
onEvent,
|
|
497
504
|
}: {
|
|
498
505
|
harness: AgentHarness;
|
|
499
|
-
runInput
|
|
506
|
+
runInput?: Parameters<AgentHarness["runWithTelemetry"]>[0];
|
|
507
|
+
events?: AsyncIterable<AgentEvent>;
|
|
500
508
|
initialContextTokens?: number;
|
|
501
509
|
initialContextWindow?: number;
|
|
502
510
|
onEvent?: (event: AgentEvent, draft: TurnDraftState) => void | Promise<void>;
|
|
@@ -512,7 +520,8 @@ const executeConversationTurn = async ({
|
|
|
512
520
|
let runSteps = 0;
|
|
513
521
|
let runMaxSteps: number | undefined;
|
|
514
522
|
|
|
515
|
-
|
|
523
|
+
const source = events ?? harness.runWithTelemetry(runInput!);
|
|
524
|
+
for await (const event of source) {
|
|
516
525
|
recordStandardTurnEvent(draft, event);
|
|
517
526
|
if (event.type === "run:started") {
|
|
518
527
|
latestRunId = event.runId;
|
|
@@ -634,6 +643,142 @@ export const __internalRunOrchestration = {
|
|
|
634
643
|
executeConversationTurn,
|
|
635
644
|
};
|
|
636
645
|
|
|
646
|
+
// ── Shared turn metadata helper ──────────────────────────────────
|
|
647
|
+
// Standardises post-run metadata persistence across all execution paths.
|
|
648
|
+
|
|
649
|
+
type TurnResultMetadata = {
|
|
650
|
+
latestRunId: string;
|
|
651
|
+
contextTokens: number;
|
|
652
|
+
contextWindow: number;
|
|
653
|
+
continuation?: boolean;
|
|
654
|
+
continuationMessages?: Message[];
|
|
655
|
+
harnessMessages?: Message[];
|
|
656
|
+
toolResultArchive?: Conversation["_toolResultArchive"];
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
const applyTurnMetadata = (
|
|
660
|
+
conv: Conversation,
|
|
661
|
+
meta: TurnResultMetadata,
|
|
662
|
+
opts: {
|
|
663
|
+
clearContinuation?: boolean;
|
|
664
|
+
clearApprovals?: boolean;
|
|
665
|
+
setIdle?: boolean;
|
|
666
|
+
shouldRebuildCanonical?: boolean;
|
|
667
|
+
} = {},
|
|
668
|
+
): void => {
|
|
669
|
+
const {
|
|
670
|
+
clearContinuation = true,
|
|
671
|
+
clearApprovals = true,
|
|
672
|
+
setIdle = true,
|
|
673
|
+
shouldRebuildCanonical = false,
|
|
674
|
+
} = opts;
|
|
675
|
+
|
|
676
|
+
if (meta.continuation && meta.continuationMessages) {
|
|
677
|
+
conv._continuationMessages = meta.continuationMessages;
|
|
678
|
+
} else if (clearContinuation) {
|
|
679
|
+
conv._continuationMessages = undefined;
|
|
680
|
+
conv._continuationCount = undefined;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (meta.harnessMessages) {
|
|
684
|
+
conv._harnessMessages = meta.harnessMessages;
|
|
685
|
+
} else if (shouldRebuildCanonical) {
|
|
686
|
+
conv._harnessMessages = conv.messages;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (meta.toolResultArchive !== undefined) {
|
|
690
|
+
conv._toolResultArchive = meta.toolResultArchive;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
conv.runtimeRunId = meta.latestRunId || conv.runtimeRunId;
|
|
694
|
+
|
|
695
|
+
if (clearApprovals) conv.pendingApprovals = [];
|
|
696
|
+
if (setIdle) conv.runStatus = "idle";
|
|
697
|
+
|
|
698
|
+
if (meta.contextTokens > 0) conv.contextTokens = meta.contextTokens;
|
|
699
|
+
if (meta.contextWindow > 0) conv.contextWindow = meta.contextWindow;
|
|
700
|
+
|
|
701
|
+
conv.updatedAt = Date.now();
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
// ── Shared cron helpers ──────────────────────────────────────────
|
|
705
|
+
// Used by both the HTTP /api/cron endpoint and the local-dev scheduler.
|
|
706
|
+
|
|
707
|
+
type CronRunResult = {
|
|
708
|
+
response: string;
|
|
709
|
+
steps: number;
|
|
710
|
+
assistantMetadata?: Message["metadata"];
|
|
711
|
+
hasContent: boolean;
|
|
712
|
+
contextTokens: number;
|
|
713
|
+
contextWindow: number;
|
|
714
|
+
harnessMessages?: Message[];
|
|
715
|
+
toolResultArchive?: Conversation["_toolResultArchive"];
|
|
716
|
+
latestRunId: string;
|
|
717
|
+
continuation: boolean;
|
|
718
|
+
continuationMessages?: Message[];
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
const runCronAgent = async (
|
|
722
|
+
harnessRef: AgentHarness,
|
|
723
|
+
task: string,
|
|
724
|
+
conversationId: string,
|
|
725
|
+
historyMessages: Message[],
|
|
726
|
+
toolResultArchive?: Conversation["_toolResultArchive"],
|
|
727
|
+
onEvent?: (event: AgentEvent) => void | Promise<void>,
|
|
728
|
+
): Promise<CronRunResult> => {
|
|
729
|
+
const execution = await executeConversationTurn({
|
|
730
|
+
harness: harnessRef,
|
|
731
|
+
runInput: {
|
|
732
|
+
task,
|
|
733
|
+
conversationId,
|
|
734
|
+
parameters: {
|
|
735
|
+
__activeConversationId: conversationId,
|
|
736
|
+
[TOOL_RESULT_ARCHIVE_PARAM]: toolResultArchive ?? {},
|
|
737
|
+
},
|
|
738
|
+
messages: historyMessages,
|
|
739
|
+
},
|
|
740
|
+
onEvent,
|
|
741
|
+
});
|
|
742
|
+
flushTurnDraft(execution.draft);
|
|
743
|
+
const hasContent = execution.draft.assistantResponse.length > 0 || execution.draft.toolTimeline.length > 0;
|
|
744
|
+
const assistantMetadata = buildAssistantMetadata(execution.draft);
|
|
745
|
+
return {
|
|
746
|
+
response: execution.draft.assistantResponse,
|
|
747
|
+
steps: execution.runSteps,
|
|
748
|
+
assistantMetadata,
|
|
749
|
+
hasContent,
|
|
750
|
+
contextTokens: execution.runContextTokens,
|
|
751
|
+
contextWindow: execution.runContextWindow,
|
|
752
|
+
harnessMessages: execution.runHarnessMessages,
|
|
753
|
+
toolResultArchive: harnessRef.getToolResultArchive(conversationId),
|
|
754
|
+
latestRunId: execution.latestRunId,
|
|
755
|
+
continuation: execution.runContinuation,
|
|
756
|
+
continuationMessages: execution.runContinuationMessages,
|
|
757
|
+
};
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
const buildCronMessages = (
|
|
761
|
+
task: string,
|
|
762
|
+
historyMessages: Message[],
|
|
763
|
+
result: CronRunResult,
|
|
764
|
+
): Message[] => [
|
|
765
|
+
...historyMessages,
|
|
766
|
+
{ role: "user" as const, content: task },
|
|
767
|
+
...(result.hasContent
|
|
768
|
+
? [{ role: "assistant" as const, content: result.response, metadata: result.assistantMetadata }]
|
|
769
|
+
: []),
|
|
770
|
+
];
|
|
771
|
+
|
|
772
|
+
/** Append a cron turn to a freshly-fetched conversation (avoids overwriting concurrent writes). */
|
|
773
|
+
const appendCronTurn = (conv: Conversation, task: string, result: CronRunResult): void => {
|
|
774
|
+
conv.messages.push(
|
|
775
|
+
{ role: "user" as const, content: task },
|
|
776
|
+
...(result.hasContent
|
|
777
|
+
? [{ role: "assistant" as const, content: result.response, metadata: result.assistantMetadata }]
|
|
778
|
+
: []),
|
|
779
|
+
);
|
|
780
|
+
};
|
|
781
|
+
|
|
637
782
|
const AGENT_TEMPLATE = (
|
|
638
783
|
name: string,
|
|
639
784
|
id: string,
|
|
@@ -2701,30 +2846,23 @@ export const createRequestHandler = async (options?: {
|
|
|
2701
2846
|
if (callbackNeedsContinuation || execution.draft.assistantResponse.length > 0 || execution.draft.toolTimeline.length > 0) {
|
|
2702
2847
|
const freshConv = await conversationStore.get(conversationId);
|
|
2703
2848
|
if (freshConv) {
|
|
2704
|
-
if (callbackNeedsContinuation) {
|
|
2705
|
-
freshConv._continuationMessages = execution.runContinuationMessages;
|
|
2706
|
-
} else {
|
|
2707
|
-
freshConv._continuationMessages = undefined;
|
|
2849
|
+
if (!callbackNeedsContinuation) {
|
|
2708
2850
|
freshConv.messages.push({
|
|
2709
2851
|
role: "assistant",
|
|
2710
2852
|
content: execution.draft.assistantResponse,
|
|
2711
2853
|
metadata: buildAssistantMetadata(execution.draft),
|
|
2712
2854
|
});
|
|
2713
2855
|
}
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2856
|
+
applyTurnMetadata(freshConv, {
|
|
2857
|
+
latestRunId: execution.latestRunId,
|
|
2858
|
+
contextTokens: execution.runContextTokens,
|
|
2859
|
+
contextWindow: execution.runContextWindow,
|
|
2860
|
+
continuation: !!callbackNeedsContinuation,
|
|
2861
|
+
continuationMessages: execution.runContinuationMessages,
|
|
2862
|
+
harnessMessages: callbackNeedsContinuation ? execution.runHarnessMessages : undefined,
|
|
2863
|
+
toolResultArchive: harness.getToolResultArchive(conversationId),
|
|
2864
|
+
}, { shouldRebuildCanonical: true, clearApprovals: false });
|
|
2723
2865
|
freshConv.runningCallbackSince = undefined;
|
|
2724
|
-
freshConv.runStatus = "idle";
|
|
2725
|
-
if (execution.runContextTokens > 0) freshConv.contextTokens = execution.runContextTokens;
|
|
2726
|
-
if (execution.runContextWindow > 0) freshConv.contextWindow = execution.runContextWindow;
|
|
2727
|
-
freshConv.updatedAt = Date.now();
|
|
2728
2866
|
await conversationStore.update(freshConv);
|
|
2729
2867
|
|
|
2730
2868
|
// Proactive messaging notification if conversation has a messaging channel
|
|
@@ -2955,15 +3093,7 @@ export const createRequestHandler = async (options?: {
|
|
|
2955
3093
|
runId: null,
|
|
2956
3094
|
});
|
|
2957
3095
|
let latestRunId = conversation.runtimeRunId ?? "";
|
|
2958
|
-
let assistantResponse = "";
|
|
2959
|
-
const toolTimeline: string[] = [];
|
|
2960
|
-
const sections: Array<{ type: "text" | "tools"; content: string | string[] }> = [];
|
|
2961
|
-
let currentText = "";
|
|
2962
|
-
let currentTools: string[] = [];
|
|
2963
3096
|
let checkpointedRun = false;
|
|
2964
|
-
let runContextTokens = conversation.contextTokens ?? 0;
|
|
2965
|
-
let runContextWindow = conversation.contextWindow ?? 0;
|
|
2966
|
-
let resumeHarnessMessages: Message[] | undefined;
|
|
2967
3097
|
|
|
2968
3098
|
const normalizedCheckpoint = normalizeApprovalCheckpoint(checkpoint, conversation.messages);
|
|
2969
3099
|
const baseMessages = normalizedCheckpoint.baseMessageCount != null
|
|
@@ -3006,136 +3136,103 @@ export const createRequestHandler = async (options?: {
|
|
|
3006
3136
|
? [...fullCheckpointMessages, resumeToolResultMsg]
|
|
3007
3137
|
: fullCheckpointMessages;
|
|
3008
3138
|
|
|
3139
|
+
let draftRef: TurnDraftState | undefined;
|
|
3140
|
+
let execution: ExecuteTurnResult | undefined;
|
|
3141
|
+
|
|
3009
3142
|
try {
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
3030
|
-
assistantResponse += " ";
|
|
3143
|
+
execution = await executeConversationTurn({
|
|
3144
|
+
harness,
|
|
3145
|
+
events: harness.continueFromToolResult({
|
|
3146
|
+
messages: fullCheckpointMessages,
|
|
3147
|
+
toolResults,
|
|
3148
|
+
conversationId,
|
|
3149
|
+
abortSignal: abortController.signal,
|
|
3150
|
+
}),
|
|
3151
|
+
initialContextTokens: conversation.contextTokens ?? 0,
|
|
3152
|
+
initialContextWindow: conversation.contextWindow ?? 0,
|
|
3153
|
+
onEvent: async (event, draft) => {
|
|
3154
|
+
draftRef = draft;
|
|
3155
|
+
if (event.type === "run:started") {
|
|
3156
|
+
latestRunId = event.runId;
|
|
3157
|
+
runOwners.set(event.runId, conversation.ownerId);
|
|
3158
|
+
runConversations.set(event.runId, conversationId);
|
|
3159
|
+
const active = activeConversationRuns.get(conversationId);
|
|
3160
|
+
if (active && active.abortController === abortController) {
|
|
3161
|
+
active.runId = event.runId;
|
|
3031
3162
|
}
|
|
3032
3163
|
}
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
if (currentText.length > 0) {
|
|
3038
|
-
sections.push({ type: "text", content: currentText });
|
|
3039
|
-
currentText = "";
|
|
3164
|
+
if (event.type === "tool:approval:required") {
|
|
3165
|
+
const toolText = `- approval required \`${event.tool}\``;
|
|
3166
|
+
draft.toolTimeline.push(toolText);
|
|
3167
|
+
draft.currentTools.push(toolText);
|
|
3040
3168
|
}
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
currentTools.push(toolText);
|
|
3054
|
-
}
|
|
3055
|
-
if (event.type === "tool:approval:required") {
|
|
3056
|
-
const toolText = `- approval required \`${event.tool}\``;
|
|
3057
|
-
toolTimeline.push(toolText);
|
|
3058
|
-
currentTools.push(toolText);
|
|
3059
|
-
}
|
|
3060
|
-
if (event.type === "tool:approval:checkpoint") {
|
|
3061
|
-
const conv = await conversationStore.get(conversationId);
|
|
3062
|
-
if (conv) {
|
|
3063
|
-
conv.pendingApprovals = buildApprovalCheckpoints({
|
|
3064
|
-
approvals: event.approvals,
|
|
3065
|
-
runId: latestRunId,
|
|
3066
|
-
checkpointMessages: [...fullCheckpointWithResults, ...event.checkpointMessages],
|
|
3067
|
-
baseMessageCount: 0,
|
|
3068
|
-
pendingToolCalls: event.pendingToolCalls,
|
|
3069
|
-
});
|
|
3070
|
-
conv.updatedAt = Date.now();
|
|
3071
|
-
await conversationStore.update(conv);
|
|
3169
|
+
if (event.type === "tool:approval:checkpoint") {
|
|
3170
|
+
const conv = await conversationStore.get(conversationId);
|
|
3171
|
+
if (conv) {
|
|
3172
|
+
conv.pendingApprovals = buildApprovalCheckpoints({
|
|
3173
|
+
approvals: event.approvals,
|
|
3174
|
+
runId: latestRunId,
|
|
3175
|
+
checkpointMessages: [...fullCheckpointWithResults, ...event.checkpointMessages],
|
|
3176
|
+
baseMessageCount: 0,
|
|
3177
|
+
pendingToolCalls: event.pendingToolCalls,
|
|
3178
|
+
});
|
|
3179
|
+
conv.updatedAt = Date.now();
|
|
3180
|
+
await conversationStore.update(conv);
|
|
3072
3181
|
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3182
|
+
if (conv.channelMeta?.platform === "telegram") {
|
|
3183
|
+
const tgAdapter = messagingAdapters.get("telegram") as TelegramAdapter | undefined;
|
|
3184
|
+
if (tgAdapter) {
|
|
3185
|
+
const messageThreadId = parseTelegramMessageThreadIdFromPlatformThreadId(
|
|
3186
|
+
conv.channelMeta.platformThreadId,
|
|
3187
|
+
conv.channelMeta.channelId,
|
|
3188
|
+
);
|
|
3189
|
+
void tgAdapter.sendApprovalRequest(
|
|
3190
|
+
conv.channelMeta.channelId,
|
|
3191
|
+
event.approvals.map(a => ({ approvalId: a.approvalId, tool: a.tool, input: a.input })),
|
|
3192
|
+
{ message_thread_id: messageThreadId },
|
|
3193
|
+
).catch(() => {});
|
|
3194
|
+
}
|
|
3085
3195
|
}
|
|
3086
3196
|
}
|
|
3197
|
+
checkpointedRun = true;
|
|
3087
3198
|
}
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
runContextWindow = event.result.contextWindow ?? runContextWindow;
|
|
3096
|
-
if (event.result.continuationMessages) {
|
|
3097
|
-
resumeHarnessMessages = event.result.continuationMessages;
|
|
3098
|
-
}
|
|
3099
|
-
}
|
|
3100
|
-
if (event.type === "run:error") {
|
|
3101
|
-
assistantResponse = assistantResponse || `[Error: ${event.error.message}]`;
|
|
3102
|
-
}
|
|
3103
|
-
await telemetry.emit(event);
|
|
3104
|
-
broadcastEvent(conversationId, event);
|
|
3105
|
-
emitBrowserStatusIfActive(conversationId, event);
|
|
3106
|
-
}
|
|
3199
|
+
await telemetry.emit(event);
|
|
3200
|
+
broadcastEvent(conversationId, event);
|
|
3201
|
+
emitBrowserStatusIfActive(conversationId, event);
|
|
3202
|
+
},
|
|
3203
|
+
});
|
|
3204
|
+
flushTurnDraft(execution.draft);
|
|
3205
|
+
latestRunId = execution.latestRunId || latestRunId;
|
|
3107
3206
|
} catch (err) {
|
|
3108
3207
|
console.error("[resume-run] error:", err instanceof Error ? err.message : err);
|
|
3109
|
-
|
|
3208
|
+
if (draftRef) {
|
|
3209
|
+
draftRef.assistantResponse = draftRef.assistantResponse || `[Error: ${err instanceof Error ? err.message : "Unknown error"}]`;
|
|
3210
|
+
flushTurnDraft(draftRef);
|
|
3211
|
+
}
|
|
3110
3212
|
}
|
|
3111
3213
|
|
|
3112
|
-
|
|
3113
|
-
sections.push({ type: "tools", content: currentTools });
|
|
3114
|
-
}
|
|
3115
|
-
if (currentText.length > 0) {
|
|
3116
|
-
sections.push({ type: "text", content: currentText });
|
|
3117
|
-
}
|
|
3214
|
+
const draft = execution?.draft ?? draftRef ?? createTurnDraftState();
|
|
3118
3215
|
|
|
3119
3216
|
if (!checkpointedRun) {
|
|
3120
3217
|
const conv = await conversationStore.get(conversationId);
|
|
3121
3218
|
if (conv) {
|
|
3122
|
-
const prevMessages = conv.messages;
|
|
3123
3219
|
const hasAssistantContent =
|
|
3124
|
-
assistantResponse.length > 0 || toolTimeline.length > 0 || sections.length > 0;
|
|
3220
|
+
draft.assistantResponse.length > 0 || draft.toolTimeline.length > 0 || draft.sections.length > 0;
|
|
3125
3221
|
if (hasAssistantContent) {
|
|
3222
|
+
const prevMessages = conv.messages;
|
|
3126
3223
|
const lastMsg = prevMessages[prevMessages.length - 1];
|
|
3127
3224
|
if (lastMsg && lastMsg.role === "assistant" && lastMsg.metadata) {
|
|
3128
3225
|
const existingToolActivity = (lastMsg.metadata as Record<string, unknown>).toolActivity;
|
|
3129
3226
|
const existingSections = (lastMsg.metadata as Record<string, unknown>).sections;
|
|
3130
3227
|
const mergedTimeline = [
|
|
3131
3228
|
...(Array.isArray(existingToolActivity) ? existingToolActivity as string[] : []),
|
|
3132
|
-
...toolTimeline,
|
|
3229
|
+
...draft.toolTimeline,
|
|
3133
3230
|
];
|
|
3134
3231
|
const mergedSections = [
|
|
3135
3232
|
...(Array.isArray(existingSections) ? existingSections as Array<{ type: "text" | "tools"; content: string | string[] }> : []),
|
|
3136
|
-
...sections,
|
|
3233
|
+
...draft.sections,
|
|
3137
3234
|
];
|
|
3138
|
-
const mergedText = (typeof lastMsg.content === "string" ? lastMsg.content : "") + assistantResponse;
|
|
3235
|
+
const mergedText = (typeof lastMsg.content === "string" ? lastMsg.content : "") + draft.assistantResponse;
|
|
3139
3236
|
conv.messages = [
|
|
3140
3237
|
...prevMessages.slice(0, -1),
|
|
3141
3238
|
{
|
|
@@ -3152,25 +3249,18 @@ export const createRequestHandler = async (options?: {
|
|
|
3152
3249
|
...prevMessages,
|
|
3153
3250
|
{
|
|
3154
3251
|
role: "assistant" as const,
|
|
3155
|
-
content: assistantResponse,
|
|
3156
|
-
metadata: (
|
|
3157
|
-
? { toolActivity: toolTimeline, sections: sections.length > 0 ? sections : undefined }
|
|
3158
|
-
: undefined) as Message["metadata"],
|
|
3252
|
+
content: draft.assistantResponse,
|
|
3253
|
+
metadata: buildAssistantMetadata(draft),
|
|
3159
3254
|
},
|
|
3160
3255
|
];
|
|
3161
3256
|
}
|
|
3162
3257
|
}
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
conv.pendingApprovals = [];
|
|
3170
|
-
conv.runStatus = "idle";
|
|
3171
|
-
if (runContextTokens > 0) conv.contextTokens = runContextTokens;
|
|
3172
|
-
if (runContextWindow > 0) conv.contextWindow = runContextWindow;
|
|
3173
|
-
conv.updatedAt = Date.now();
|
|
3258
|
+
applyTurnMetadata(conv, {
|
|
3259
|
+
latestRunId,
|
|
3260
|
+
contextTokens: execution?.runContextTokens ?? 0,
|
|
3261
|
+
contextWindow: execution?.runContextWindow ?? 0,
|
|
3262
|
+
harnessMessages: execution?.runHarnessMessages,
|
|
3263
|
+
}, { shouldRebuildCanonical: true });
|
|
3174
3264
|
await conversationStore.update(conv);
|
|
3175
3265
|
}
|
|
3176
3266
|
} else {
|
|
@@ -3341,7 +3431,7 @@ export const createRequestHandler = async (options?: {
|
|
|
3341
3431
|
conversationId,
|
|
3342
3432
|
messages: historyMessages,
|
|
3343
3433
|
files: input.files,
|
|
3344
|
-
parameters: {
|
|
3434
|
+
parameters: withToolResultArchiveParam({
|
|
3345
3435
|
...(input.metadata ? {
|
|
3346
3436
|
__messaging_platform: input.metadata.platform,
|
|
3347
3437
|
__messaging_sender_id: input.metadata.sender.id,
|
|
@@ -3349,7 +3439,7 @@ export const createRequestHandler = async (options?: {
|
|
|
3349
3439
|
__messaging_thread_id: input.metadata.threadId,
|
|
3350
3440
|
} : {}),
|
|
3351
3441
|
__activeConversationId: conversationId,
|
|
3352
|
-
},
|
|
3442
|
+
}, latestConversation ?? { _toolResultArchive: {} } as Conversation),
|
|
3353
3443
|
};
|
|
3354
3444
|
|
|
3355
3445
|
try {
|
|
@@ -3448,31 +3538,31 @@ export const createRequestHandler = async (options?: {
|
|
|
3448
3538
|
|
|
3449
3539
|
if (!checkpointedRun) {
|
|
3450
3540
|
await updateConversation((c) => {
|
|
3451
|
-
if (runContinuation && runContinuationMessages) {
|
|
3452
|
-
c._continuationMessages = runContinuationMessages;
|
|
3453
|
-
} else {
|
|
3454
|
-
c._continuationMessages = undefined;
|
|
3541
|
+
if (!(runContinuation && runContinuationMessages)) {
|
|
3455
3542
|
c.messages = buildMessages();
|
|
3456
3543
|
}
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
c.runStatus = "idle";
|
|
3467
|
-
if (runContextTokens > 0) c.contextTokens = runContextTokens;
|
|
3468
|
-
if (runContextWindow > 0) c.contextWindow = runContextWindow;
|
|
3544
|
+
applyTurnMetadata(c, {
|
|
3545
|
+
latestRunId,
|
|
3546
|
+
contextTokens: runContextTokens,
|
|
3547
|
+
contextWindow: runContextWindow,
|
|
3548
|
+
continuation: runContinuation,
|
|
3549
|
+
continuationMessages: runContinuationMessages,
|
|
3550
|
+
harnessMessages: runContinuationMessages,
|
|
3551
|
+
toolResultArchive: harness.getToolResultArchive(conversationId),
|
|
3552
|
+
}, { shouldRebuildCanonical: true });
|
|
3469
3553
|
});
|
|
3470
3554
|
} else {
|
|
3471
3555
|
await updateConversation((c) => {
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3556
|
+
applyTurnMetadata(c, {
|
|
3557
|
+
latestRunId: "",
|
|
3558
|
+
contextTokens: 0,
|
|
3559
|
+
contextWindow: 0,
|
|
3560
|
+
toolResultArchive: harness.getToolResultArchive(conversationId),
|
|
3561
|
+
}, {
|
|
3562
|
+
clearContinuation: false,
|
|
3563
|
+
clearApprovals: false,
|
|
3564
|
+
shouldRebuildCanonical: shouldRebuildCanonical && !c._harnessMessages?.length,
|
|
3565
|
+
});
|
|
3476
3566
|
});
|
|
3477
3567
|
}
|
|
3478
3568
|
finishConversationStream(conversationId);
|
|
@@ -3648,9 +3738,10 @@ export const createRequestHandler = async (options?: {
|
|
|
3648
3738
|
// ── Unified continuation ──────────────────────────────────────────────
|
|
3649
3739
|
const MAX_CONTINUATION_COUNT = 20;
|
|
3650
3740
|
|
|
3651
|
-
async function
|
|
3741
|
+
async function runContinuation(
|
|
3652
3742
|
conversationId: string,
|
|
3653
|
-
|
|
3743
|
+
onYield?: (event: AgentEvent) => void | Promise<void>,
|
|
3744
|
+
): Promise<void> {
|
|
3654
3745
|
const conversation = await conversationStore.get(conversationId);
|
|
3655
3746
|
if (!conversation) return;
|
|
3656
3747
|
if (Array.isArray(conversation.pendingApprovals) && conversation.pendingApprovals.length > 0) return;
|
|
@@ -3693,9 +3784,11 @@ export const createRequestHandler = async (options?: {
|
|
|
3693
3784
|
|
|
3694
3785
|
try {
|
|
3695
3786
|
if (conversation.parentConversationId) {
|
|
3696
|
-
|
|
3787
|
+
for await (const event of runSubagentContinuation(conversationId, conversation, continuationMessages)) {
|
|
3788
|
+
if (onYield) await onYield(event);
|
|
3789
|
+
}
|
|
3697
3790
|
} else {
|
|
3698
|
-
|
|
3791
|
+
await runChatContinuation(conversationId, conversation, continuationMessages, onYield);
|
|
3699
3792
|
}
|
|
3700
3793
|
} finally {
|
|
3701
3794
|
activeConversationRuns.delete(conversationId);
|
|
@@ -3703,136 +3796,66 @@ export const createRequestHandler = async (options?: {
|
|
|
3703
3796
|
}
|
|
3704
3797
|
}
|
|
3705
3798
|
|
|
3706
|
-
async function
|
|
3799
|
+
async function runChatContinuation(
|
|
3707
3800
|
conversationId: string,
|
|
3708
3801
|
conversation: Conversation,
|
|
3709
3802
|
continuationMessages: Message[],
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
if (event.type === "run:started") {
|
|
3732
|
-
latestRunId = event.runId;
|
|
3733
|
-
runOwners.set(event.runId, conversation.ownerId);
|
|
3734
|
-
runConversations.set(event.runId, conversationId);
|
|
3735
|
-
const active = activeConversationRuns.get(conversationId);
|
|
3736
|
-
if (active) active.runId = event.runId;
|
|
3737
|
-
}
|
|
3738
|
-
if (event.type === "model:chunk") {
|
|
3739
|
-
if (currentTools.length > 0) {
|
|
3740
|
-
sections.push({ type: "tools", content: currentTools });
|
|
3741
|
-
currentTools = [];
|
|
3742
|
-
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
3743
|
-
assistantResponse += " ";
|
|
3744
|
-
}
|
|
3745
|
-
}
|
|
3746
|
-
assistantResponse += event.content;
|
|
3747
|
-
currentText += event.content;
|
|
3748
|
-
}
|
|
3749
|
-
if (event.type === "tool:started") {
|
|
3750
|
-
if (currentText.length > 0) {
|
|
3751
|
-
sections.push({ type: "text", content: currentText });
|
|
3752
|
-
currentText = "";
|
|
3753
|
-
}
|
|
3754
|
-
const toolText = `- start \`${event.tool}\``;
|
|
3755
|
-
toolTimeline.push(toolText);
|
|
3756
|
-
currentTools.push(toolText);
|
|
3757
|
-
}
|
|
3758
|
-
if (event.type === "tool:completed") {
|
|
3759
|
-
const toolText = `- done \`${event.tool}\` (${event.duration}ms)`;
|
|
3760
|
-
toolTimeline.push(toolText);
|
|
3761
|
-
currentTools.push(toolText);
|
|
3762
|
-
}
|
|
3763
|
-
if (event.type === "tool:error") {
|
|
3764
|
-
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
3765
|
-
toolTimeline.push(toolText);
|
|
3766
|
-
currentTools.push(toolText);
|
|
3767
|
-
}
|
|
3768
|
-
if (event.type === "run:completed") {
|
|
3769
|
-
runContextTokens = event.result.contextTokens ?? runContextTokens;
|
|
3770
|
-
runContextWindow = event.result.contextWindow ?? runContextWindow;
|
|
3771
|
-
if (event.result.continuation && event.result.continuationMessages) {
|
|
3772
|
-
nextContinuationMessages = event.result.continuationMessages;
|
|
3773
|
-
}
|
|
3774
|
-
if (event.result.continuationMessages) {
|
|
3775
|
-
nextHarnessMessages = event.result.continuationMessages;
|
|
3776
|
-
}
|
|
3777
|
-
if (!assistantResponse && event.result.response) {
|
|
3778
|
-
assistantResponse = event.result.response;
|
|
3803
|
+
onYield?: (event: AgentEvent) => void | Promise<void>,
|
|
3804
|
+
): Promise<void> {
|
|
3805
|
+
const execution = await executeConversationTurn({
|
|
3806
|
+
harness,
|
|
3807
|
+
runInput: {
|
|
3808
|
+
conversationId,
|
|
3809
|
+
parameters: withToolResultArchiveParam({
|
|
3810
|
+
__activeConversationId: conversationId,
|
|
3811
|
+
__ownerId: conversation.ownerId,
|
|
3812
|
+
}, conversation),
|
|
3813
|
+
messages: continuationMessages,
|
|
3814
|
+
abortSignal: activeConversationRuns.get(conversationId)?.abortController.signal,
|
|
3815
|
+
},
|
|
3816
|
+
initialContextTokens: conversation.contextTokens ?? 0,
|
|
3817
|
+
initialContextWindow: conversation.contextWindow ?? 0,
|
|
3818
|
+
onEvent: async (event) => {
|
|
3819
|
+
if (event.type === "run:started") {
|
|
3820
|
+
runOwners.set(event.runId, conversation.ownerId);
|
|
3821
|
+
runConversations.set(event.runId, conversationId);
|
|
3822
|
+
const active = activeConversationRuns.get(conversationId);
|
|
3823
|
+
if (active) active.runId = event.runId;
|
|
3779
3824
|
}
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
}
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
yield event;
|
|
3787
|
-
}
|
|
3788
|
-
|
|
3789
|
-
if (currentTools.length > 0) sections.push({ type: "tools", content: currentTools });
|
|
3790
|
-
if (currentText.length > 0) sections.push({ type: "text", content: currentText });
|
|
3825
|
+
await telemetry.emit(event);
|
|
3826
|
+
broadcastEvent(conversationId, event);
|
|
3827
|
+
if (onYield) await onYield(event);
|
|
3828
|
+
},
|
|
3829
|
+
});
|
|
3830
|
+
flushTurnDraft(execution.draft);
|
|
3791
3831
|
|
|
3792
3832
|
const freshConv = await conversationStore.get(conversationId);
|
|
3793
3833
|
if (!freshConv) return;
|
|
3794
3834
|
|
|
3795
|
-
const hasContent = assistantResponse.length > 0 || toolTimeline.length > 0;
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3835
|
+
const hasContent = execution.draft.assistantResponse.length > 0 || execution.draft.toolTimeline.length > 0;
|
|
3836
|
+
if (hasContent) {
|
|
3837
|
+
freshConv.messages = [
|
|
3838
|
+
...freshConv.messages,
|
|
3839
|
+
{
|
|
3840
|
+
role: "assistant" as const,
|
|
3841
|
+
content: execution.draft.assistantResponse,
|
|
3842
|
+
metadata: buildAssistantMetadata(execution.draft),
|
|
3843
|
+
},
|
|
3844
|
+
];
|
|
3845
|
+
}
|
|
3803
3846
|
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3847
|
+
applyTurnMetadata(freshConv, {
|
|
3848
|
+
latestRunId: execution.latestRunId,
|
|
3849
|
+
contextTokens: execution.runContextTokens,
|
|
3850
|
+
contextWindow: execution.runContextWindow,
|
|
3851
|
+
continuation: execution.runContinuation,
|
|
3852
|
+
continuationMessages: execution.runContinuationMessages,
|
|
3853
|
+
harnessMessages: execution.runHarnessMessages,
|
|
3854
|
+
toolResultArchive: harness.getToolResultArchive(conversationId),
|
|
3855
|
+
}, { shouldRebuildCanonical: true });
|
|
3856
|
+
if (execution.runContinuation) {
|
|
3812
3857
|
freshConv._continuationCount = conversation._continuationCount;
|
|
3813
|
-
} else {
|
|
3814
|
-
if (hasContent) {
|
|
3815
|
-
freshConv.messages = [
|
|
3816
|
-
...freshConv.messages,
|
|
3817
|
-
{ role: "assistant", content: assistantResponse, metadata: assistantMetadata },
|
|
3818
|
-
];
|
|
3819
|
-
}
|
|
3820
|
-
freshConv._continuationMessages = undefined;
|
|
3821
|
-
freshConv._continuationCount = undefined;
|
|
3822
3858
|
}
|
|
3823
|
-
|
|
3824
|
-
if (nextHarnessMessages) {
|
|
3825
|
-
freshConv._harnessMessages = nextHarnessMessages;
|
|
3826
|
-
} else {
|
|
3827
|
-
freshConv._harnessMessages = freshConv.messages;
|
|
3828
|
-
}
|
|
3829
|
-
freshConv._toolResultArchive = harness.getToolResultArchive(conversationId);
|
|
3830
|
-
freshConv.runtimeRunId = latestRunId || freshConv.runtimeRunId;
|
|
3831
|
-
freshConv.pendingApprovals = [];
|
|
3832
|
-
if (runContextTokens > 0) freshConv.contextTokens = runContextTokens;
|
|
3833
|
-
if (runContextWindow > 0) freshConv.contextWindow = runContextWindow;
|
|
3834
|
-
freshConv.runStatus = "idle";
|
|
3835
|
-
freshConv.updatedAt = Date.now();
|
|
3836
3859
|
await conversationStore.update(freshConv);
|
|
3837
3860
|
}
|
|
3838
3861
|
|
|
@@ -4165,17 +4188,22 @@ export const createRequestHandler = async (options?: {
|
|
|
4165
4188
|
// Regular (non-subagent) approval
|
|
4166
4189
|
const found = await findPendingApproval(approvalId, "local-owner");
|
|
4167
4190
|
let foundConversation = found?.conversation;
|
|
4168
|
-
|
|
4191
|
+
const foundApproval = found?.approval;
|
|
4169
4192
|
|
|
4170
4193
|
if (!foundConversation || !foundApproval) {
|
|
4171
4194
|
console.warn("[telegram-approval] approval not found:", approvalId);
|
|
4172
4195
|
return;
|
|
4173
4196
|
}
|
|
4174
|
-
foundApproval = normalizeApprovalCheckpoint(foundApproval, foundConversation.messages);
|
|
4175
4197
|
|
|
4176
|
-
|
|
4198
|
+
const approvalDecision = approved ? "approved" as const : "denied" as const;
|
|
4199
|
+
await adapter.updateApprovalMessage(approvalId, approvalDecision, foundApproval.tool);
|
|
4177
4200
|
|
|
4178
|
-
|
|
4201
|
+
foundConversation.pendingApprovals = (foundConversation.pendingApprovals ?? []).map((approval) =>
|
|
4202
|
+
approval.approvalId === approvalId
|
|
4203
|
+
? { ...normalizeApprovalCheckpoint(approval, foundConversation!.messages), decision: approvalDecision }
|
|
4204
|
+
: normalizeApprovalCheckpoint(approval, foundConversation!.messages),
|
|
4205
|
+
);
|
|
4206
|
+
await conversationStore.update(foundConversation);
|
|
4179
4207
|
|
|
4180
4208
|
broadcastEvent(foundConversation.conversationId,
|
|
4181
4209
|
approved
|
|
@@ -4183,15 +4211,16 @@ export const createRequestHandler = async (options?: {
|
|
|
4183
4211
|
: { type: "tool:approval:denied", approvalId },
|
|
4184
4212
|
);
|
|
4185
4213
|
|
|
4186
|
-
const
|
|
4187
|
-
|
|
4214
|
+
const refreshedConversation = await conversationStore.get(foundConversation.conversationId);
|
|
4215
|
+
const allApprovals = (refreshedConversation?.pendingApprovals ?? []).map((approval) =>
|
|
4216
|
+
normalizeApprovalCheckpoint(approval, refreshedConversation!.messages),
|
|
4188
4217
|
);
|
|
4189
4218
|
const allDecided = allApprovals.length > 0 && allApprovals.every(a => a.decision != null);
|
|
4190
4219
|
|
|
4191
4220
|
if (!allDecided) {
|
|
4192
|
-
await conversationStore.update(foundConversation);
|
|
4193
4221
|
return;
|
|
4194
4222
|
}
|
|
4223
|
+
foundConversation = refreshedConversation!;
|
|
4195
4224
|
|
|
4196
4225
|
// All decided — resume the run
|
|
4197
4226
|
const conversationId = foundConversation.conversationId;
|
|
@@ -4471,9 +4500,7 @@ export const createRequestHandler = async (options?: {
|
|
|
4471
4500
|
writeJson(response, 202, { ok: true });
|
|
4472
4501
|
const work = (async () => {
|
|
4473
4502
|
try {
|
|
4474
|
-
|
|
4475
|
-
// Events are already broadcast inside runContinuation
|
|
4476
|
-
}
|
|
4503
|
+
await runContinuation(conversationId);
|
|
4477
4504
|
// Chain: if another continuation is needed, fire next self-fetch
|
|
4478
4505
|
const conv = await conversationStore.get(conversationId);
|
|
4479
4506
|
if (conv?._continuationMessages?.length) {
|
|
@@ -5202,7 +5229,7 @@ export const createRequestHandler = async (options?: {
|
|
|
5202
5229
|
writeJson(response, 200, {
|
|
5203
5230
|
conversation: {
|
|
5204
5231
|
...conversation,
|
|
5205
|
-
messages: conversation.messages.map(normalizeMessageForClient),
|
|
5232
|
+
messages: conversation.messages.map(normalizeMessageForClient).filter((m): m is Message => m !== null),
|
|
5206
5233
|
pendingApprovals: storedPending,
|
|
5207
5234
|
_continuationMessages: undefined,
|
|
5208
5235
|
_harnessMessages: undefined,
|
|
@@ -5406,7 +5433,7 @@ export const createRequestHandler = async (options?: {
|
|
|
5406
5433
|
|
|
5407
5434
|
let eventCount = 0;
|
|
5408
5435
|
try {
|
|
5409
|
-
|
|
5436
|
+
await runContinuation(conversationId, async (event) => {
|
|
5410
5437
|
eventCount++;
|
|
5411
5438
|
let sseEvent: AgentEvent = event;
|
|
5412
5439
|
if (sseEvent.type === "run:completed") {
|
|
@@ -5420,7 +5447,7 @@ export const createRequestHandler = async (options?: {
|
|
|
5420
5447
|
// Client disconnected — continue processing so the run completes
|
|
5421
5448
|
}
|
|
5422
5449
|
emitBrowserStatusIfActive(conversationId, event, response);
|
|
5423
|
-
}
|
|
5450
|
+
});
|
|
5424
5451
|
} catch (err) {
|
|
5425
5452
|
const errorEvent: AgentEvent = {
|
|
5426
5453
|
type: "run:error",
|
|
@@ -5546,18 +5573,6 @@ export const createRequestHandler = async (options?: {
|
|
|
5546
5573
|
`[poncho] conversation="${conversationId}" history_source=${canonicalHistory.source}`,
|
|
5547
5574
|
);
|
|
5548
5575
|
let latestRunId = conversation.runtimeRunId ?? "";
|
|
5549
|
-
let assistantResponse = "";
|
|
5550
|
-
const toolTimeline: string[] = [];
|
|
5551
|
-
const sections: Array<{ type: "text" | "tools"; content: string | string[] }> = [];
|
|
5552
|
-
let currentText = "";
|
|
5553
|
-
let currentTools: string[] = [];
|
|
5554
|
-
let runCancelled = false;
|
|
5555
|
-
let checkpointedRun = false;
|
|
5556
|
-
let didCompact = false;
|
|
5557
|
-
let runContextTokens = conversation.contextTokens ?? 0;
|
|
5558
|
-
let runContextWindow = conversation.contextWindow ?? 0;
|
|
5559
|
-
let runContinuationMessages: Message[] | undefined;
|
|
5560
|
-
let runHarnessMessages: Message[] | undefined;
|
|
5561
5576
|
let userContent: Message["content"] | undefined = messageText;
|
|
5562
5577
|
if (files.length > 0) {
|
|
5563
5578
|
try {
|
|
@@ -5593,14 +5608,51 @@ export const createRequestHandler = async (options?: {
|
|
|
5593
5608
|
return;
|
|
5594
5609
|
}
|
|
5595
5610
|
}
|
|
5596
|
-
// Forward subagent lifecycle events to the response so the client can update the sidebar.
|
|
5597
|
-
// These events are broadcast via broadcastEvent but not emitted by the harness run loop.
|
|
5598
5611
|
const unsubSubagentEvents = onConversationEvent(conversationId, (evt) => {
|
|
5599
5612
|
if (evt.type.startsWith("subagent:")) {
|
|
5600
5613
|
try { response.write(formatSseEvent(evt)); } catch {}
|
|
5601
5614
|
}
|
|
5602
5615
|
});
|
|
5603
5616
|
|
|
5617
|
+
const draft = createTurnDraftState();
|
|
5618
|
+
let checkpointedRun = false;
|
|
5619
|
+
let runCancelled = false;
|
|
5620
|
+
let runContinuationMessages: Message[] | undefined;
|
|
5621
|
+
|
|
5622
|
+
const buildMessages = (): Message[] => {
|
|
5623
|
+
const draftSections = cloneSections(draft.sections);
|
|
5624
|
+
if (draft.currentTools.length > 0) {
|
|
5625
|
+
draftSections.push({ type: "tools", content: [...draft.currentTools] });
|
|
5626
|
+
}
|
|
5627
|
+
if (draft.currentText.length > 0) {
|
|
5628
|
+
draftSections.push({ type: "text", content: draft.currentText });
|
|
5629
|
+
}
|
|
5630
|
+
const userTurn: Message[] = userContent != null
|
|
5631
|
+
? [{ role: "user" as const, content: userContent }]
|
|
5632
|
+
: [];
|
|
5633
|
+
const hasDraftContent =
|
|
5634
|
+
draft.assistantResponse.length > 0 || draft.toolTimeline.length > 0 || draftSections.length > 0;
|
|
5635
|
+
if (!hasDraftContent) {
|
|
5636
|
+
return [...historyMessages, ...userTurn];
|
|
5637
|
+
}
|
|
5638
|
+
return [
|
|
5639
|
+
...historyMessages,
|
|
5640
|
+
...userTurn,
|
|
5641
|
+
{
|
|
5642
|
+
role: "assistant" as const,
|
|
5643
|
+
content: draft.assistantResponse,
|
|
5644
|
+
metadata: buildAssistantMetadata(draft, draftSections),
|
|
5645
|
+
},
|
|
5646
|
+
];
|
|
5647
|
+
};
|
|
5648
|
+
|
|
5649
|
+
const persistDraftAssistantTurn = async (): Promise<void> => {
|
|
5650
|
+
if (draft.assistantResponse.length === 0 && draft.toolTimeline.length === 0) return;
|
|
5651
|
+
conversation.messages = buildMessages();
|
|
5652
|
+
conversation.updatedAt = Date.now();
|
|
5653
|
+
await conversationStore.update(conversation);
|
|
5654
|
+
};
|
|
5655
|
+
|
|
5604
5656
|
try {
|
|
5605
5657
|
{
|
|
5606
5658
|
conversation.messages = [...historyMessages, { role: "user", content: userContent! }];
|
|
@@ -5612,43 +5664,6 @@ export const createRequestHandler = async (options?: {
|
|
|
5612
5664
|
});
|
|
5613
5665
|
}
|
|
5614
5666
|
|
|
5615
|
-
const persistDraftAssistantTurn = async (): Promise<void> => {
|
|
5616
|
-
const draftSections: Array<{ type: "text" | "tools"; content: string | string[] }> = [
|
|
5617
|
-
...sections.map((section) => ({
|
|
5618
|
-
type: section.type,
|
|
5619
|
-
content: Array.isArray(section.content) ? [...section.content] : section.content,
|
|
5620
|
-
})),
|
|
5621
|
-
];
|
|
5622
|
-
if (currentTools.length > 0) {
|
|
5623
|
-
draftSections.push({ type: "tools", content: [...currentTools] });
|
|
5624
|
-
}
|
|
5625
|
-
if (currentText.length > 0) {
|
|
5626
|
-
draftSections.push({ type: "text", content: currentText });
|
|
5627
|
-
}
|
|
5628
|
-
const hasDraftContent =
|
|
5629
|
-
assistantResponse.length > 0 || toolTimeline.length > 0 || draftSections.length > 0;
|
|
5630
|
-
if (!hasDraftContent) {
|
|
5631
|
-
return;
|
|
5632
|
-
}
|
|
5633
|
-
conversation.messages = [
|
|
5634
|
-
...historyMessages,
|
|
5635
|
-
...(userContent != null ? [{ role: "user" as const, content: userContent }] : []),
|
|
5636
|
-
{
|
|
5637
|
-
role: "assistant",
|
|
5638
|
-
content: assistantResponse,
|
|
5639
|
-
metadata:
|
|
5640
|
-
toolTimeline.length > 0 || draftSections.length > 0
|
|
5641
|
-
? ({
|
|
5642
|
-
toolActivity: [...toolTimeline],
|
|
5643
|
-
sections: draftSections.length > 0 ? draftSections : undefined,
|
|
5644
|
-
} as Message["metadata"])
|
|
5645
|
-
: undefined,
|
|
5646
|
-
},
|
|
5647
|
-
];
|
|
5648
|
-
conversation.updatedAt = Date.now();
|
|
5649
|
-
await conversationStore.update(conversation);
|
|
5650
|
-
};
|
|
5651
|
-
|
|
5652
5667
|
let cachedRecallCorpus: unknown[] | undefined;
|
|
5653
5668
|
const lazyRecallCorpus = async () => {
|
|
5654
5669
|
if (cachedRecallCorpus) return cachedRecallCorpus;
|
|
@@ -5682,281 +5697,168 @@ export const createRequestHandler = async (options?: {
|
|
|
5682
5697
|
return cachedRecallCorpus;
|
|
5683
5698
|
};
|
|
5684
5699
|
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
runCancelled = true;
|
|
5709
|
-
}
|
|
5710
|
-
if (event.type === "model:chunk") {
|
|
5711
|
-
if (currentTools.length > 0) {
|
|
5712
|
-
sections.push({ type: "tools", content: currentTools });
|
|
5713
|
-
currentTools = [];
|
|
5714
|
-
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
5715
|
-
assistantResponse += " ";
|
|
5716
|
-
}
|
|
5717
|
-
}
|
|
5718
|
-
assistantResponse += event.content;
|
|
5719
|
-
currentText += event.content;
|
|
5720
|
-
}
|
|
5721
|
-
if (event.type === "tool:started") {
|
|
5722
|
-
// If we have text accumulated, push it as a text section
|
|
5723
|
-
if (currentText.length > 0) {
|
|
5724
|
-
sections.push({ type: "text", content: currentText });
|
|
5725
|
-
currentText = "";
|
|
5726
|
-
}
|
|
5727
|
-
const toolText = `- start \`${event.tool}\``;
|
|
5728
|
-
toolTimeline.push(toolText);
|
|
5729
|
-
currentTools.push(toolText);
|
|
5730
|
-
}
|
|
5731
|
-
if (event.type === "tool:completed") {
|
|
5732
|
-
const toolText = `- done \`${event.tool}\` (${event.duration}ms)`;
|
|
5733
|
-
toolTimeline.push(toolText);
|
|
5734
|
-
currentTools.push(toolText);
|
|
5735
|
-
}
|
|
5736
|
-
if (event.type === "tool:error") {
|
|
5737
|
-
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
5738
|
-
toolTimeline.push(toolText);
|
|
5739
|
-
currentTools.push(toolText);
|
|
5740
|
-
}
|
|
5741
|
-
if (event.type === "compaction:completed") {
|
|
5742
|
-
didCompact = true;
|
|
5743
|
-
if (event.compactedMessages) {
|
|
5744
|
-
historyMessages.length = 0;
|
|
5745
|
-
historyMessages.push(...event.compactedMessages);
|
|
5700
|
+
const execution = await executeConversationTurn({
|
|
5701
|
+
harness,
|
|
5702
|
+
runInput: {
|
|
5703
|
+
task: messageText,
|
|
5704
|
+
conversationId,
|
|
5705
|
+
parameters: withToolResultArchiveParam({
|
|
5706
|
+
...(bodyParameters ?? {}),
|
|
5707
|
+
__conversationRecallCorpus: lazyRecallCorpus,
|
|
5708
|
+
__activeConversationId: conversationId,
|
|
5709
|
+
__ownerId: ownerId,
|
|
5710
|
+
}, conversation),
|
|
5711
|
+
messages: harnessMessages,
|
|
5712
|
+
files: files.length > 0 ? files : undefined,
|
|
5713
|
+
abortSignal: abortController.signal,
|
|
5714
|
+
},
|
|
5715
|
+
initialContextTokens: conversation.contextTokens ?? 0,
|
|
5716
|
+
initialContextWindow: conversation.contextWindow ?? 0,
|
|
5717
|
+
onEvent: async (event, eventDraft) => {
|
|
5718
|
+
draft.assistantResponse = eventDraft.assistantResponse;
|
|
5719
|
+
draft.toolTimeline = eventDraft.toolTimeline;
|
|
5720
|
+
draft.sections = eventDraft.sections;
|
|
5721
|
+
draft.currentTools = eventDraft.currentTools;
|
|
5722
|
+
draft.currentText = eventDraft.currentText;
|
|
5746
5723
|
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
}
|
|
5756
|
-
if (event.type === "step:completed") {
|
|
5757
|
-
await persistDraftAssistantTurn();
|
|
5758
|
-
}
|
|
5759
|
-
if (event.type === "tool:approval:required") {
|
|
5760
|
-
const toolText = `- approval required \`${event.tool}\``;
|
|
5761
|
-
toolTimeline.push(toolText);
|
|
5762
|
-
currentTools.push(toolText);
|
|
5763
|
-
const existingApprovals = Array.isArray(conversation.pendingApprovals)
|
|
5764
|
-
? conversation.pendingApprovals
|
|
5765
|
-
: [];
|
|
5766
|
-
if (!existingApprovals.some((approval) => approval.approvalId === event.approvalId)) {
|
|
5767
|
-
conversation.pendingApprovals = [
|
|
5768
|
-
...existingApprovals,
|
|
5769
|
-
{
|
|
5770
|
-
approvalId: event.approvalId,
|
|
5771
|
-
runId: latestRunId || conversation.runtimeRunId || "",
|
|
5772
|
-
tool: event.tool,
|
|
5773
|
-
toolCallId: undefined,
|
|
5774
|
-
input: (event.input ?? {}) as Record<string, unknown>,
|
|
5775
|
-
checkpointMessages: undefined,
|
|
5776
|
-
baseMessageCount: historyMessages.length,
|
|
5777
|
-
pendingToolCalls: [],
|
|
5778
|
-
},
|
|
5779
|
-
];
|
|
5780
|
-
conversation.updatedAt = Date.now();
|
|
5781
|
-
await conversationStore.update(conversation);
|
|
5724
|
+
if (event.type === "run:started") {
|
|
5725
|
+
latestRunId = event.runId;
|
|
5726
|
+
runOwners.set(event.runId, ownerId);
|
|
5727
|
+
runConversations.set(event.runId, conversationId);
|
|
5728
|
+
const active = activeConversationRuns.get(conversationId);
|
|
5729
|
+
if (active && active.abortController === abortController) {
|
|
5730
|
+
active.runId = event.runId;
|
|
5731
|
+
}
|
|
5782
5732
|
}
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
if (event.type === "tool:approval:checkpoint") {
|
|
5786
|
-
const checkpointSections = [...sections];
|
|
5787
|
-
if (currentTools.length > 0) {
|
|
5788
|
-
checkpointSections.push({ type: "tools", content: [...currentTools] });
|
|
5733
|
+
if (event.type === "run:cancelled") {
|
|
5734
|
+
runCancelled = true;
|
|
5789
5735
|
}
|
|
5790
|
-
if (
|
|
5791
|
-
|
|
5736
|
+
if (event.type === "compaction:completed") {
|
|
5737
|
+
if (event.compactedMessages) {
|
|
5738
|
+
historyMessages.length = 0;
|
|
5739
|
+
historyMessages.push(...event.compactedMessages);
|
|
5740
|
+
|
|
5741
|
+
const preservedFromHistory = historyMessages.length - 1;
|
|
5742
|
+
const removedCount = preRunMessages.length - Math.max(0, preservedFromHistory);
|
|
5743
|
+
const existingHistory = conversation.compactedHistory ?? [];
|
|
5744
|
+
conversation.compactedHistory = [
|
|
5745
|
+
...existingHistory,
|
|
5746
|
+
...preRunMessages.slice(0, removedCount),
|
|
5747
|
+
];
|
|
5748
|
+
}
|
|
5792
5749
|
}
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
...(userContent != null ? [{ role: "user" as const, content: userContent }] : []),
|
|
5796
|
-
...(assistantResponse.length > 0 || toolTimeline.length > 0 || checkpointSections.length > 0
|
|
5797
|
-
? [{
|
|
5798
|
-
role: "assistant" as const,
|
|
5799
|
-
content: assistantResponse,
|
|
5800
|
-
metadata: (toolTimeline.length > 0 || checkpointSections.length > 0
|
|
5801
|
-
? { toolActivity: [...toolTimeline], sections: checkpointSections.length > 0 ? checkpointSections : undefined }
|
|
5802
|
-
: undefined) as Message["metadata"],
|
|
5803
|
-
}]
|
|
5804
|
-
: []),
|
|
5805
|
-
];
|
|
5806
|
-
conversation.pendingApprovals = buildApprovalCheckpoints({
|
|
5807
|
-
approvals: event.approvals,
|
|
5808
|
-
runId: latestRunId,
|
|
5809
|
-
checkpointMessages: event.checkpointMessages,
|
|
5810
|
-
baseMessageCount: historyMessages.length,
|
|
5811
|
-
pendingToolCalls: event.pendingToolCalls,
|
|
5812
|
-
});
|
|
5813
|
-
conversation._toolResultArchive = harness.getToolResultArchive(conversationId);
|
|
5814
|
-
conversation.updatedAt = Date.now();
|
|
5815
|
-
await conversationStore.update(conversation);
|
|
5816
|
-
checkpointedRun = true;
|
|
5817
|
-
}
|
|
5818
|
-
if (event.type === "run:completed") {
|
|
5819
|
-
if (assistantResponse.length === 0 && event.result.response) {
|
|
5820
|
-
assistantResponse = event.result.response;
|
|
5750
|
+
if (event.type === "step:completed") {
|
|
5751
|
+
await persistDraftAssistantTurn();
|
|
5821
5752
|
}
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5753
|
+
if (event.type === "tool:approval:required") {
|
|
5754
|
+
const toolText = `- approval required \`${event.tool}\``;
|
|
5755
|
+
draft.toolTimeline.push(toolText);
|
|
5756
|
+
draft.currentTools.push(toolText);
|
|
5757
|
+
const existingApprovals = Array.isArray(conversation.pendingApprovals)
|
|
5758
|
+
? conversation.pendingApprovals
|
|
5759
|
+
: [];
|
|
5760
|
+
if (!existingApprovals.some((approval) => approval.approvalId === event.approvalId)) {
|
|
5761
|
+
conversation.pendingApprovals = [
|
|
5762
|
+
...existingApprovals,
|
|
5763
|
+
{
|
|
5764
|
+
approvalId: event.approvalId,
|
|
5765
|
+
runId: latestRunId || conversation.runtimeRunId || "",
|
|
5766
|
+
tool: event.tool,
|
|
5767
|
+
toolCallId: undefined,
|
|
5768
|
+
input: (event.input ?? {}) as Record<string, unknown>,
|
|
5769
|
+
checkpointMessages: undefined,
|
|
5770
|
+
baseMessageCount: historyMessages.length,
|
|
5771
|
+
pendingToolCalls: [],
|
|
5772
|
+
},
|
|
5773
|
+
];
|
|
5774
|
+
conversation.updatedAt = Date.now();
|
|
5775
|
+
await conversationStore.update(conversation);
|
|
5776
|
+
}
|
|
5777
|
+
await persistDraftAssistantTurn();
|
|
5826
5778
|
}
|
|
5827
|
-
if (event.
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
const intMetadata = toolTimeline.length > 0 || intSections.length > 0
|
|
5837
|
-
? ({ toolActivity: [...toolTimeline], sections: intSections.length > 0 ? intSections : undefined } as Message["metadata"])
|
|
5838
|
-
: undefined;
|
|
5839
|
-
conversation.messages = [
|
|
5840
|
-
...historyMessages,
|
|
5841
|
-
...(userContent != null ? [{ role: "user" as const, content: userContent }] : []),
|
|
5842
|
-
...(hasContent ? [{ role: "assistant" as const, content: assistantResponse, metadata: intMetadata }] : []),
|
|
5843
|
-
];
|
|
5844
|
-
conversation._continuationMessages = runContinuationMessages;
|
|
5845
|
-
conversation._harnessMessages = runContinuationMessages;
|
|
5779
|
+
if (event.type === "tool:approval:checkpoint") {
|
|
5780
|
+
conversation.messages = buildMessages();
|
|
5781
|
+
conversation.pendingApprovals = buildApprovalCheckpoints({
|
|
5782
|
+
approvals: event.approvals,
|
|
5783
|
+
runId: latestRunId,
|
|
5784
|
+
checkpointMessages: event.checkpointMessages,
|
|
5785
|
+
baseMessageCount: historyMessages.length,
|
|
5786
|
+
pendingToolCalls: event.pendingToolCalls,
|
|
5787
|
+
});
|
|
5846
5788
|
conversation._toolResultArchive = harness.getToolResultArchive(conversationId);
|
|
5847
|
-
conversation.runtimeRunId = latestRunId || conversation.runtimeRunId;
|
|
5848
|
-
if (!checkpointedRun) {
|
|
5849
|
-
conversation.pendingApprovals = [];
|
|
5850
|
-
}
|
|
5851
|
-
if (runContextTokens > 0) conversation.contextTokens = runContextTokens;
|
|
5852
|
-
if (runContextWindow > 0) conversation.contextWindow = runContextWindow;
|
|
5853
5789
|
conversation.updatedAt = Date.now();
|
|
5854
5790
|
await conversationStore.update(conversation);
|
|
5791
|
+
checkpointedRun = true;
|
|
5792
|
+
}
|
|
5793
|
+
if (event.type === "run:completed") {
|
|
5794
|
+
if (event.result.continuation && event.result.continuationMessages) {
|
|
5795
|
+
runContinuationMessages = event.result.continuationMessages;
|
|
5796
|
+
|
|
5797
|
+
conversation.messages = buildMessages();
|
|
5798
|
+
conversation._continuationMessages = runContinuationMessages;
|
|
5799
|
+
conversation._harnessMessages = runContinuationMessages;
|
|
5800
|
+
conversation._toolResultArchive = harness.getToolResultArchive(conversationId);
|
|
5801
|
+
conversation.runtimeRunId = latestRunId || conversation.runtimeRunId;
|
|
5802
|
+
if (!checkpointedRun) {
|
|
5803
|
+
conversation.pendingApprovals = [];
|
|
5804
|
+
}
|
|
5805
|
+
if ((event.result.contextTokens ?? 0) > 0) conversation.contextTokens = event.result.contextTokens!;
|
|
5806
|
+
if ((event.result.contextWindow ?? 0) > 0) conversation.contextWindow = event.result.contextWindow!;
|
|
5807
|
+
conversation.updatedAt = Date.now();
|
|
5808
|
+
await conversationStore.update(conversation);
|
|
5809
|
+
|
|
5810
|
+
if (!checkpointedRun) {
|
|
5811
|
+
doWaitUntil(
|
|
5812
|
+
new Promise(r => setTimeout(r, 3000)).then(() =>
|
|
5813
|
+
selfFetchWithRetry(`/api/internal/continue/${encodeURIComponent(conversationId)}`),
|
|
5814
|
+
),
|
|
5815
|
+
);
|
|
5816
|
+
}
|
|
5817
|
+
}
|
|
5818
|
+
}
|
|
5855
5819
|
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5820
|
+
await telemetry.emit(event);
|
|
5821
|
+
let sseEvent: AgentEvent = event.type === "compaction:completed" && event.compactedMessages
|
|
5822
|
+
? { ...event, compactedMessages: undefined }
|
|
5823
|
+
: event;
|
|
5824
|
+
if (sseEvent.type === "run:completed") {
|
|
5825
|
+
const hasPendingSubagents = await hasPendingSubagentWorkForParent(conversationId, ownerId);
|
|
5826
|
+
const stripped = { ...sseEvent, result: { ...sseEvent.result, continuationMessages: undefined } };
|
|
5827
|
+
if (hasPendingSubagents) {
|
|
5828
|
+
sseEvent = { ...stripped, pendingSubagents: true };
|
|
5829
|
+
} else {
|
|
5830
|
+
sseEvent = stripped;
|
|
5864
5831
|
}
|
|
5865
5832
|
}
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
if (sseEvent.type === "run:completed") {
|
|
5872
|
-
const hasPendingSubagents = await hasPendingSubagentWorkForParent(conversationId, ownerId);
|
|
5873
|
-
const stripped = { ...sseEvent, result: { ...sseEvent.result, continuationMessages: undefined } };
|
|
5874
|
-
if (hasPendingSubagents) {
|
|
5875
|
-
sseEvent = { ...stripped, pendingSubagents: true };
|
|
5876
|
-
} else {
|
|
5877
|
-
sseEvent = stripped;
|
|
5833
|
+
broadcastEvent(conversationId, sseEvent);
|
|
5834
|
+
try {
|
|
5835
|
+
response.write(formatSseEvent(sseEvent));
|
|
5836
|
+
} catch {
|
|
5837
|
+
// Client disconnected — continue processing so the run completes.
|
|
5878
5838
|
}
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
}
|
|
5887
|
-
emitBrowserStatusIfActive(conversationId, event, response);
|
|
5888
|
-
}
|
|
5889
|
-
// Finalize sections
|
|
5890
|
-
if (currentTools.length > 0) {
|
|
5891
|
-
sections.push({ type: "tools", content: currentTools });
|
|
5892
|
-
}
|
|
5893
|
-
if (currentText.length > 0) {
|
|
5894
|
-
sections.push({ type: "text", content: currentText });
|
|
5895
|
-
}
|
|
5839
|
+
emitBrowserStatusIfActive(conversationId, event, response);
|
|
5840
|
+
},
|
|
5841
|
+
});
|
|
5842
|
+
|
|
5843
|
+
flushTurnDraft(draft);
|
|
5844
|
+
latestRunId = execution.latestRunId || latestRunId;
|
|
5845
|
+
|
|
5896
5846
|
if (!checkpointedRun && !runContinuationMessages) {
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
:
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
...userTurn,
|
|
5906
|
-
{
|
|
5907
|
-
role: "assistant",
|
|
5908
|
-
content: assistantResponse,
|
|
5909
|
-
metadata:
|
|
5910
|
-
toolTimeline.length > 0 || sections.length > 0
|
|
5911
|
-
? ({
|
|
5912
|
-
toolActivity: toolTimeline,
|
|
5913
|
-
sections: sections.length > 0 ? sections : undefined,
|
|
5914
|
-
} as Message["metadata"])
|
|
5915
|
-
: undefined,
|
|
5916
|
-
},
|
|
5917
|
-
]
|
|
5918
|
-
: [...historyMessages, ...userTurn];
|
|
5919
|
-
conversation._continuationMessages = undefined;
|
|
5920
|
-
if (runHarnessMessages) {
|
|
5921
|
-
conversation._harnessMessages = runHarnessMessages;
|
|
5922
|
-
} else if (shouldRebuildCanonical) {
|
|
5923
|
-
conversation._harnessMessages = conversation.messages;
|
|
5924
|
-
} else {
|
|
5925
|
-
conversation._harnessMessages = conversation.messages;
|
|
5926
|
-
}
|
|
5927
|
-
conversation._toolResultArchive = harness.getToolResultArchive(conversationId);
|
|
5928
|
-
conversation.runtimeRunId = latestRunId || conversation.runtimeRunId;
|
|
5929
|
-
conversation.pendingApprovals = [];
|
|
5930
|
-
if (runContextTokens > 0) conversation.contextTokens = runContextTokens;
|
|
5931
|
-
if (runContextWindow > 0) conversation.contextWindow = runContextWindow;
|
|
5932
|
-
conversation.updatedAt = Date.now();
|
|
5847
|
+
conversation.messages = buildMessages();
|
|
5848
|
+
applyTurnMetadata(conversation, {
|
|
5849
|
+
latestRunId,
|
|
5850
|
+
contextTokens: execution.runContextTokens,
|
|
5851
|
+
contextWindow: execution.runContextWindow,
|
|
5852
|
+
harnessMessages: execution.runHarnessMessages,
|
|
5853
|
+
toolResultArchive: harness.getToolResultArchive(conversationId),
|
|
5854
|
+
}, { shouldRebuildCanonical });
|
|
5933
5855
|
await conversationStore.update(conversation);
|
|
5934
5856
|
}
|
|
5935
5857
|
} catch (error) {
|
|
5858
|
+
flushTurnDraft(draft);
|
|
5936
5859
|
if (abortController.signal.aborted || runCancelled) {
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
fallbackSections.push({ type: "tools", content: [...currentTools] });
|
|
5940
|
-
}
|
|
5941
|
-
if (currentText.length > 0) {
|
|
5942
|
-
fallbackSections.push({ type: "text", content: currentText });
|
|
5943
|
-
}
|
|
5944
|
-
if (assistantResponse.length > 0 || toolTimeline.length > 0 || fallbackSections.length > 0) {
|
|
5945
|
-
conversation.messages = [
|
|
5946
|
-
...historyMessages,
|
|
5947
|
-
...(userContent != null ? [{ role: "user" as const, content: userContent }] : []),
|
|
5948
|
-
{
|
|
5949
|
-
role: "assistant",
|
|
5950
|
-
content: assistantResponse,
|
|
5951
|
-
metadata:
|
|
5952
|
-
toolTimeline.length > 0 || fallbackSections.length > 0
|
|
5953
|
-
? ({
|
|
5954
|
-
toolActivity: [...toolTimeline],
|
|
5955
|
-
sections: fallbackSections.length > 0 ? fallbackSections : undefined,
|
|
5956
|
-
} as Message["metadata"])
|
|
5957
|
-
: undefined,
|
|
5958
|
-
},
|
|
5959
|
-
];
|
|
5860
|
+
if (draft.assistantResponse.length > 0 || draft.toolTimeline.length > 0 || draft.sections.length > 0) {
|
|
5861
|
+
conversation.messages = buildMessages();
|
|
5960
5862
|
conversation.updatedAt = Date.now();
|
|
5961
5863
|
await conversationStore.update(conversation);
|
|
5962
5864
|
}
|
|
@@ -5977,29 +5879,8 @@ export const createRequestHandler = async (options?: {
|
|
|
5977
5879
|
}),
|
|
5978
5880
|
);
|
|
5979
5881
|
} catch {
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
fallbackSections.push({ type: "tools", content: [...currentTools] });
|
|
5983
|
-
}
|
|
5984
|
-
if (currentText.length > 0) {
|
|
5985
|
-
fallbackSections.push({ type: "text", content: currentText });
|
|
5986
|
-
}
|
|
5987
|
-
if (assistantResponse.length > 0 || toolTimeline.length > 0 || fallbackSections.length > 0) {
|
|
5988
|
-
conversation.messages = [
|
|
5989
|
-
...historyMessages,
|
|
5990
|
-
...(userContent != null ? [{ role: "user" as const, content: userContent }] : []),
|
|
5991
|
-
{
|
|
5992
|
-
role: "assistant",
|
|
5993
|
-
content: assistantResponse,
|
|
5994
|
-
metadata:
|
|
5995
|
-
toolTimeline.length > 0 || fallbackSections.length > 0
|
|
5996
|
-
? ({
|
|
5997
|
-
toolActivity: [...toolTimeline],
|
|
5998
|
-
sections: fallbackSections.length > 0 ? fallbackSections : undefined,
|
|
5999
|
-
} as Message["metadata"])
|
|
6000
|
-
: undefined,
|
|
6001
|
-
},
|
|
6002
|
-
];
|
|
5882
|
+
if (draft.assistantResponse.length > 0 || draft.toolTimeline.length > 0 || draft.sections.length > 0) {
|
|
5883
|
+
conversation.messages = buildMessages();
|
|
6003
5884
|
conversation.updatedAt = Date.now();
|
|
6004
5885
|
await conversationStore.update(conversation);
|
|
6005
5886
|
}
|
|
@@ -6015,10 +5896,6 @@ export const createRequestHandler = async (options?: {
|
|
|
6015
5896
|
runConversations.delete(latestRunId);
|
|
6016
5897
|
}
|
|
6017
5898
|
|
|
6018
|
-
// Determine if subagent work is pending before deciding to close the
|
|
6019
|
-
// event stream. When a callback is about to run, the stream stays open
|
|
6020
|
-
// so clients that subscribe to /events receive callback-run events in
|
|
6021
|
-
// real-time — the same delivery path used for every other run.
|
|
6022
5899
|
const hadDeferred = pendingCallbackNeeded.delete(conversationId);
|
|
6023
5900
|
const freshConv = await conversationStore.get(conversationId);
|
|
6024
5901
|
const needsCallback = hadDeferred || !!freshConv?.pendingSubagentResults?.length;
|
|
@@ -6106,53 +5983,37 @@ export const createRequestHandler = async (options?: {
|
|
|
6106
5983
|
});
|
|
6107
5984
|
const historyMessages = [...historySelection.messages];
|
|
6108
5985
|
try {
|
|
6109
|
-
const
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
}
|
|
6123
|
-
|
|
6124
|
-
const assistantResponse = execution.draft.assistantResponse;
|
|
6125
|
-
|
|
6126
|
-
conv.messages = [
|
|
6127
|
-
...historyMessages,
|
|
6128
|
-
{ role: "user" as const, content: task },
|
|
6129
|
-
...(assistantResponse ? [{ role: "assistant" as const, content: assistantResponse }] : []),
|
|
6130
|
-
];
|
|
6131
|
-
if (execution.runHarnessMessages) {
|
|
6132
|
-
conv._harnessMessages = execution.runHarnessMessages;
|
|
6133
|
-
} else if (historySelection.shouldRebuildCanonical) {
|
|
6134
|
-
conv._harnessMessages = conv.messages;
|
|
5986
|
+
const result = await runCronAgent(harness, task, conv.conversationId, historyMessages,
|
|
5987
|
+
conv._toolResultArchive,
|
|
5988
|
+
async (event) => { await telemetry.emit(event); },
|
|
5989
|
+
);
|
|
5990
|
+
|
|
5991
|
+
const freshConv = await conversationStore.get(conv.conversationId);
|
|
5992
|
+
if (freshConv) {
|
|
5993
|
+
appendCronTurn(freshConv, task, result);
|
|
5994
|
+
applyTurnMetadata(freshConv, result, {
|
|
5995
|
+
clearContinuation: false,
|
|
5996
|
+
clearApprovals: false,
|
|
5997
|
+
setIdle: false,
|
|
5998
|
+
shouldRebuildCanonical: historySelection.shouldRebuildCanonical,
|
|
5999
|
+
});
|
|
6000
|
+
await conversationStore.update(freshConv);
|
|
6135
6001
|
}
|
|
6136
|
-
conv._toolResultArchive = harness.getToolResultArchive(conv.conversationId);
|
|
6137
|
-
if (execution.runContextTokens > 0) conv.contextTokens = execution.runContextTokens;
|
|
6138
|
-
if (execution.runContextWindow > 0) conv.contextWindow = execution.runContextWindow;
|
|
6139
|
-
conv.updatedAt = Date.now();
|
|
6140
|
-
await conversationStore.update(conv);
|
|
6141
6002
|
|
|
6142
|
-
if (
|
|
6003
|
+
if (result.response) {
|
|
6143
6004
|
try {
|
|
6144
6005
|
await adapter.sendReply(
|
|
6145
6006
|
{
|
|
6146
6007
|
channelId: chatId,
|
|
6147
|
-
platformThreadId: conv.channelMeta?.platformThreadId ?? chatId,
|
|
6008
|
+
platformThreadId: (freshConv ?? conv).channelMeta?.platformThreadId ?? chatId,
|
|
6148
6009
|
},
|
|
6149
|
-
|
|
6010
|
+
result.response,
|
|
6150
6011
|
);
|
|
6151
6012
|
} catch (sendError) {
|
|
6152
6013
|
console.error(`[cron] ${jobName}: send to ${chatId} failed:`, sendError instanceof Error ? sendError.message : sendError);
|
|
6153
6014
|
}
|
|
6154
6015
|
}
|
|
6155
|
-
chatResults.push({ chatId, status: "completed", steps:
|
|
6016
|
+
chatResults.push({ chatId, status: "completed", steps: result.steps });
|
|
6156
6017
|
} catch (runError) {
|
|
6157
6018
|
chatResults.push({ chatId, status: "error" });
|
|
6158
6019
|
console.error(`[cron] ${jobName}: run for chat ${chatId} failed:`, runError instanceof Error ? runError.message : runError);
|
|
@@ -6180,7 +6041,6 @@ export const createRequestHandler = async (options?: {
|
|
|
6180
6041
|
cronOwnerId,
|
|
6181
6042
|
`[cron] ${jobName} ${timestamp}`,
|
|
6182
6043
|
);
|
|
6183
|
-
const historyMessages: Message[] = [];
|
|
6184
6044
|
|
|
6185
6045
|
const convId = conversation.conversationId;
|
|
6186
6046
|
activeConversationRuns.set(convId, {
|
|
@@ -6190,152 +6050,45 @@ export const createRequestHandler = async (options?: {
|
|
|
6190
6050
|
});
|
|
6191
6051
|
|
|
6192
6052
|
try {
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
for await (const event of harness.runWithTelemetry({
|
|
6212
|
-
task: cronJob.task,
|
|
6213
|
-
conversationId: convId,
|
|
6214
|
-
parameters: withToolResultArchiveParam({ __activeConversationId: convId }, conversation),
|
|
6215
|
-
messages: historyMessages,
|
|
6216
|
-
abortSignal: abortController.signal,
|
|
6217
|
-
})) {
|
|
6218
|
-
if (event.type === "run:started") {
|
|
6219
|
-
latestRunId = event.runId;
|
|
6220
|
-
}
|
|
6221
|
-
if (event.type === "model:chunk") {
|
|
6222
|
-
if (currentTools.length > 0) {
|
|
6223
|
-
sections.push({ type: "tools", content: currentTools });
|
|
6224
|
-
currentTools = [];
|
|
6225
|
-
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
6226
|
-
assistantResponse += " ";
|
|
6227
|
-
}
|
|
6228
|
-
}
|
|
6229
|
-
assistantResponse += event.content;
|
|
6230
|
-
currentText += event.content;
|
|
6231
|
-
}
|
|
6232
|
-
if (event.type === "tool:started") {
|
|
6233
|
-
if (currentText.length > 0) {
|
|
6234
|
-
sections.push({ type: "text", content: currentText });
|
|
6235
|
-
currentText = "";
|
|
6236
|
-
}
|
|
6237
|
-
const toolText = `- start \`${event.tool}\``;
|
|
6238
|
-
toolTimeline.push(toolText);
|
|
6239
|
-
currentTools.push(toolText);
|
|
6240
|
-
}
|
|
6241
|
-
if (event.type === "tool:completed") {
|
|
6242
|
-
const toolText = `- done \`${event.tool}\` (${event.duration}ms)`;
|
|
6243
|
-
toolTimeline.push(toolText);
|
|
6244
|
-
currentTools.push(toolText);
|
|
6245
|
-
}
|
|
6246
|
-
if (event.type === "tool:error") {
|
|
6247
|
-
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
6248
|
-
toolTimeline.push(toolText);
|
|
6249
|
-
currentTools.push(toolText);
|
|
6250
|
-
}
|
|
6251
|
-
if (event.type === "run:completed") {
|
|
6252
|
-
runResult = {
|
|
6253
|
-
status: event.result.status,
|
|
6254
|
-
steps: event.result.steps,
|
|
6255
|
-
continuation: event.result.continuation,
|
|
6256
|
-
contextTokens: event.result.contextTokens,
|
|
6257
|
-
contextWindow: event.result.contextWindow,
|
|
6258
|
-
harnessMessages: event.result.continuationMessages,
|
|
6259
|
-
};
|
|
6260
|
-
if (event.result.continuation && event.result.continuationMessages) {
|
|
6261
|
-
runContinuationMessages = event.result.continuationMessages;
|
|
6262
|
-
}
|
|
6263
|
-
if (!assistantResponse && event.result.response) {
|
|
6264
|
-
assistantResponse = event.result.response;
|
|
6265
|
-
}
|
|
6053
|
+
const result = await runCronAgent(harness, cronJob.task, convId, [],
|
|
6054
|
+
conversation._toolResultArchive,
|
|
6055
|
+
async (event) => {
|
|
6056
|
+
broadcastEvent(convId, event);
|
|
6057
|
+
await telemetry.emit(event);
|
|
6058
|
+
},
|
|
6059
|
+
);
|
|
6060
|
+
finishConversationStream(convId);
|
|
6061
|
+
|
|
6062
|
+
const freshConv = await conversationStore.get(convId);
|
|
6063
|
+
if (freshConv) {
|
|
6064
|
+
freshConv.messages = buildCronMessages(cronJob.task, [], result);
|
|
6065
|
+
applyTurnMetadata(freshConv, result, {
|
|
6066
|
+
clearApprovals: false,
|
|
6067
|
+
setIdle: false,
|
|
6068
|
+
});
|
|
6069
|
+
await conversationStore.update(freshConv);
|
|
6266
6070
|
}
|
|
6267
|
-
broadcastEvent(convId, event);
|
|
6268
|
-
await telemetry.emit(event);
|
|
6269
|
-
}
|
|
6270
6071
|
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
// pendingSubagentResults appended during the run.
|
|
6283
|
-
const hasContent = assistantResponse.length > 0 || toolTimeline.length > 0;
|
|
6284
|
-
const assistantMetadata =
|
|
6285
|
-
toolTimeline.length > 0 || sections.length > 0
|
|
6286
|
-
? ({
|
|
6287
|
-
toolActivity: [...toolTimeline],
|
|
6288
|
-
sections: sections.length > 0 ? sections : undefined,
|
|
6289
|
-
} as Message["metadata"])
|
|
6290
|
-
: undefined;
|
|
6291
|
-
const messages: Message[] = [
|
|
6292
|
-
...historyMessages,
|
|
6293
|
-
{ role: "user" as const, content: cronJob.task },
|
|
6294
|
-
...(hasContent
|
|
6295
|
-
? [{ role: "assistant" as const, content: assistantResponse, metadata: assistantMetadata }]
|
|
6296
|
-
: []),
|
|
6297
|
-
];
|
|
6298
|
-
const freshConv = await conversationStore.get(convId);
|
|
6299
|
-
if (freshConv) {
|
|
6300
|
-
// Always persist intermediate messages so clients see progress
|
|
6301
|
-
freshConv.messages = messages;
|
|
6302
|
-
if (runContinuationMessages) {
|
|
6303
|
-
freshConv._continuationMessages = runContinuationMessages;
|
|
6304
|
-
} else {
|
|
6305
|
-
freshConv._continuationMessages = undefined;
|
|
6306
|
-
freshConv._continuationCount = undefined;
|
|
6307
|
-
}
|
|
6308
|
-
if (runResult.harnessMessages) {
|
|
6309
|
-
freshConv._harnessMessages = runResult.harnessMessages;
|
|
6072
|
+
if (result.continuation) {
|
|
6073
|
+
const work = selfFetchWithRetry(`/api/internal/continue/${encodeURIComponent(convId)}`).catch(err =>
|
|
6074
|
+
console.error(`[poncho][cron] Continuation self-fetch failed:`, err instanceof Error ? err.message : err),
|
|
6075
|
+
);
|
|
6076
|
+
doWaitUntil(work);
|
|
6077
|
+
writeJson(response, 200, {
|
|
6078
|
+
conversationId: convId,
|
|
6079
|
+
status: "continued",
|
|
6080
|
+
duration: Date.now() - start,
|
|
6081
|
+
});
|
|
6082
|
+
return;
|
|
6310
6083
|
}
|
|
6311
|
-
freshConv._toolResultArchive = harness.getToolResultArchive(convId);
|
|
6312
|
-
freshConv.runtimeRunId = latestRunId || freshConv.runtimeRunId;
|
|
6313
|
-
if (runResult.contextTokens) freshConv.contextTokens = runResult.contextTokens;
|
|
6314
|
-
if (runResult.contextWindow) freshConv.contextWindow = runResult.contextWindow;
|
|
6315
|
-
freshConv.updatedAt = Date.now();
|
|
6316
|
-
await conversationStore.update(freshConv);
|
|
6317
|
-
}
|
|
6318
6084
|
|
|
6319
|
-
if (runResult.continuation) {
|
|
6320
|
-
const work = selfFetchWithRetry(`/api/internal/continue/${encodeURIComponent(convId)}`).catch(err =>
|
|
6321
|
-
console.error(`[poncho][cron] Continuation self-fetch failed:`, err instanceof Error ? err.message : err),
|
|
6322
|
-
);
|
|
6323
|
-
doWaitUntil(work);
|
|
6324
6085
|
writeJson(response, 200, {
|
|
6325
6086
|
conversationId: convId,
|
|
6326
|
-
status: "
|
|
6087
|
+
status: "completed",
|
|
6088
|
+
response: result.response.slice(0, 500),
|
|
6327
6089
|
duration: Date.now() - start,
|
|
6090
|
+
steps: result.steps,
|
|
6328
6091
|
});
|
|
6329
|
-
return;
|
|
6330
|
-
}
|
|
6331
|
-
|
|
6332
|
-
writeJson(response, 200, {
|
|
6333
|
-
conversationId: convId,
|
|
6334
|
-
status: runResult.status,
|
|
6335
|
-
response: assistantResponse.slice(0, 500),
|
|
6336
|
-
duration: Date.now() - start,
|
|
6337
|
-
steps: runResult.steps,
|
|
6338
|
-
});
|
|
6339
6092
|
} finally {
|
|
6340
6093
|
activeConversationRuns.delete(convId);
|
|
6341
6094
|
const hadDeferred = pendingCallbackNeeded.delete(convId);
|
|
@@ -6417,26 +6170,19 @@ export const createRequestHandler = async (options?: {
|
|
|
6417
6170
|
if (channelMeta) {
|
|
6418
6171
|
const adapter = messagingAdapters.get(channelMeta.platform);
|
|
6419
6172
|
if (adapter && originConv) {
|
|
6420
|
-
const
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
messages: historyMessages,
|
|
6427
|
-
})) {
|
|
6428
|
-
if (event.type === "model:chunk") {
|
|
6429
|
-
assistantResponse += event.content;
|
|
6430
|
-
}
|
|
6431
|
-
}
|
|
6432
|
-
if (assistantResponse) {
|
|
6173
|
+
const result = await runCronAgent(
|
|
6174
|
+
harness, framedMessage, originConv.conversationId,
|
|
6175
|
+
originConv.messages ?? [],
|
|
6176
|
+
originConv._toolResultArchive,
|
|
6177
|
+
);
|
|
6178
|
+
if (result.response) {
|
|
6433
6179
|
try {
|
|
6434
6180
|
await adapter.sendReply(
|
|
6435
6181
|
{
|
|
6436
6182
|
channelId: channelMeta.channelId,
|
|
6437
6183
|
platformThreadId: channelMeta.platformThreadId ?? channelMeta.channelId,
|
|
6438
6184
|
},
|
|
6439
|
-
|
|
6185
|
+
result.response,
|
|
6440
6186
|
);
|
|
6441
6187
|
} catch (sendError) {
|
|
6442
6188
|
console.error(`[reminder] Send to ${channelMeta.platform} failed:`, sendError instanceof Error ? sendError.message : sendError);
|
|
@@ -6444,12 +6190,12 @@ export const createRequestHandler = async (options?: {
|
|
|
6444
6190
|
}
|
|
6445
6191
|
const freshConv = await conversationStore.get(originConv.conversationId);
|
|
6446
6192
|
if (freshConv) {
|
|
6447
|
-
freshConv
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6193
|
+
appendCronTurn(freshConv, framedMessage, result);
|
|
6194
|
+
applyTurnMetadata(freshConv, result, {
|
|
6195
|
+
clearContinuation: false,
|
|
6196
|
+
clearApprovals: false,
|
|
6197
|
+
setIdle: false,
|
|
6198
|
+
});
|
|
6453
6199
|
await conversationStore.update(freshConv);
|
|
6454
6200
|
}
|
|
6455
6201
|
}
|
|
@@ -6460,24 +6206,15 @@ export const createRequestHandler = async (options?: {
|
|
|
6460
6206
|
`[reminder] ${reminder.task.slice(0, 80)} ${timestamp}`,
|
|
6461
6207
|
);
|
|
6462
6208
|
const convId = conversation.conversationId;
|
|
6463
|
-
|
|
6464
|
-
for await (const event of harness.runWithTelemetry({
|
|
6465
|
-
task: framedMessage,
|
|
6466
|
-
conversationId: convId,
|
|
6467
|
-
parameters: { __activeConversationId: convId },
|
|
6468
|
-
messages: [],
|
|
6469
|
-
})) {
|
|
6470
|
-
if (event.type === "model:chunk") {
|
|
6471
|
-
assistantResponse += event.content;
|
|
6472
|
-
}
|
|
6473
|
-
}
|
|
6209
|
+
const result = await runCronAgent(harness, framedMessage, convId, []);
|
|
6474
6210
|
const freshConv = await conversationStore.get(convId);
|
|
6475
6211
|
if (freshConv) {
|
|
6476
|
-
freshConv.messages = [
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6212
|
+
freshConv.messages = buildCronMessages(framedMessage, [], result);
|
|
6213
|
+
applyTurnMetadata(freshConv, result, {
|
|
6214
|
+
clearContinuation: false,
|
|
6215
|
+
clearApprovals: false,
|
|
6216
|
+
setIdle: false,
|
|
6217
|
+
});
|
|
6481
6218
|
await conversationStore.update(freshConv);
|
|
6482
6219
|
}
|
|
6483
6220
|
}
|
|
@@ -6568,65 +6305,6 @@ export const startDevServer = async (
|
|
|
6568
6305
|
type CronJob = InstanceType<typeof Cron>;
|
|
6569
6306
|
let activeJobs: CronJob[] = [];
|
|
6570
6307
|
|
|
6571
|
-
type CronRunResult = {
|
|
6572
|
-
response: string;
|
|
6573
|
-
steps: number;
|
|
6574
|
-
assistantMetadata?: Message["metadata"];
|
|
6575
|
-
hasContent: boolean;
|
|
6576
|
-
contextTokens: number;
|
|
6577
|
-
contextWindow: number;
|
|
6578
|
-
harnessMessages?: Message[];
|
|
6579
|
-
toolResultArchive?: Conversation["_toolResultArchive"];
|
|
6580
|
-
};
|
|
6581
|
-
|
|
6582
|
-
const runCronAgent = async (
|
|
6583
|
-
harnessRef: AgentHarness,
|
|
6584
|
-
task: string,
|
|
6585
|
-
conversationId: string,
|
|
6586
|
-
historyMessages: Message[],
|
|
6587
|
-
toolResultArchive?: Conversation["_toolResultArchive"],
|
|
6588
|
-
onEvent?: (event: AgentEvent) => void | Promise<void>,
|
|
6589
|
-
): Promise<CronRunResult> => {
|
|
6590
|
-
const execution = await executeConversationTurn({
|
|
6591
|
-
harness: harnessRef,
|
|
6592
|
-
runInput: {
|
|
6593
|
-
task,
|
|
6594
|
-
conversationId,
|
|
6595
|
-
parameters: {
|
|
6596
|
-
__activeConversationId: conversationId,
|
|
6597
|
-
[TOOL_RESULT_ARCHIVE_PARAM]: toolResultArchive ?? {},
|
|
6598
|
-
},
|
|
6599
|
-
messages: historyMessages,
|
|
6600
|
-
},
|
|
6601
|
-
onEvent,
|
|
6602
|
-
});
|
|
6603
|
-
flushTurnDraft(execution.draft);
|
|
6604
|
-
const hasContent = execution.draft.assistantResponse.length > 0 || execution.draft.toolTimeline.length > 0;
|
|
6605
|
-
const assistantMetadata = buildAssistantMetadata(execution.draft);
|
|
6606
|
-
return {
|
|
6607
|
-
response: execution.draft.assistantResponse,
|
|
6608
|
-
steps: execution.runSteps,
|
|
6609
|
-
assistantMetadata,
|
|
6610
|
-
hasContent,
|
|
6611
|
-
contextTokens: execution.runContextTokens,
|
|
6612
|
-
contextWindow: execution.runContextWindow,
|
|
6613
|
-
harnessMessages: execution.runHarnessMessages,
|
|
6614
|
-
toolResultArchive: harnessRef.getToolResultArchive(conversationId),
|
|
6615
|
-
};
|
|
6616
|
-
};
|
|
6617
|
-
|
|
6618
|
-
const buildCronMessages = (
|
|
6619
|
-
task: string,
|
|
6620
|
-
historyMessages: Message[],
|
|
6621
|
-
result: CronRunResult,
|
|
6622
|
-
): Message[] => [
|
|
6623
|
-
...historyMessages,
|
|
6624
|
-
{ role: "user" as const, content: task },
|
|
6625
|
-
...(result.hasContent
|
|
6626
|
-
? [{ role: "assistant" as const, content: result.response, metadata: result.assistantMetadata }]
|
|
6627
|
-
: []),
|
|
6628
|
-
];
|
|
6629
|
-
|
|
6630
6308
|
const scheduleCronJobs = (jobs: Record<string, CronJobConfig>): void => {
|
|
6631
6309
|
for (const job of activeJobs) {
|
|
6632
6310
|
job.stop();
|
|
@@ -6704,18 +6382,13 @@ export const startDevServer = async (
|
|
|
6704
6382
|
|
|
6705
6383
|
const freshConv = await store.get(convId);
|
|
6706
6384
|
if (freshConv) {
|
|
6707
|
-
freshConv
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
freshConv._toolResultArchive = result.toolResultArchive;
|
|
6715
|
-
}
|
|
6716
|
-
if (result.contextTokens > 0) freshConv.contextTokens = result.contextTokens;
|
|
6717
|
-
if (result.contextWindow > 0) freshConv.contextWindow = result.contextWindow;
|
|
6718
|
-
freshConv.updatedAt = Date.now();
|
|
6385
|
+
appendCronTurn(freshConv, task, result);
|
|
6386
|
+
applyTurnMetadata(freshConv, result, {
|
|
6387
|
+
clearContinuation: false,
|
|
6388
|
+
clearApprovals: false,
|
|
6389
|
+
setIdle: false,
|
|
6390
|
+
shouldRebuildCanonical: historySelection.shouldRebuildCanonical,
|
|
6391
|
+
});
|
|
6719
6392
|
await store.update(freshConv);
|
|
6720
6393
|
|
|
6721
6394
|
if (result.response) {
|
|
@@ -6780,15 +6453,11 @@ export const startDevServer = async (
|
|
|
6780
6453
|
const freshConv = await store.get(cronConvId);
|
|
6781
6454
|
if (freshConv) {
|
|
6782
6455
|
freshConv.messages = buildCronMessages(config.task, [], result);
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
}
|
|
6789
|
-
if (result.contextTokens > 0) freshConv.contextTokens = result.contextTokens;
|
|
6790
|
-
if (result.contextWindow > 0) freshConv.contextWindow = result.contextWindow;
|
|
6791
|
-
freshConv.updatedAt = Date.now();
|
|
6456
|
+
applyTurnMetadata(freshConv, result, {
|
|
6457
|
+
clearContinuation: false,
|
|
6458
|
+
clearApprovals: false,
|
|
6459
|
+
setIdle: false,
|
|
6460
|
+
});
|
|
6792
6461
|
await store.update(freshConv);
|
|
6793
6462
|
}
|
|
6794
6463
|
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|