@standardagents/builder 0.39.0 → 0.39.1
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/dist/built-in-routes.js +178 -67
- package/dist/built-in-routes.js.map +1 -1
- package/dist/index.js +269 -142
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +15 -33
- package/dist/runtime.js +269 -142
- package/dist/runtime.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1498,6 +1498,9 @@ function normalizeToolCallMessageSequence(messages) {
|
|
|
1498
1498
|
for (const message of messages) {
|
|
1499
1499
|
const isAssistantToolCallMessage = message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
|
|
1500
1500
|
if (!pending) {
|
|
1501
|
+
if (message.role === "tool") {
|
|
1502
|
+
continue;
|
|
1503
|
+
}
|
|
1501
1504
|
if (!isAssistantToolCallMessage) {
|
|
1502
1505
|
normalized.push(message);
|
|
1503
1506
|
continue;
|
|
@@ -7644,28 +7647,12 @@ This usually points to something I can't fix from here \u2014 for example the wo
|
|
|
7644
7647
|
state.promptPath = [sideAPrompt.name];
|
|
7645
7648
|
state.pendingImmediateAfterHandoff = true;
|
|
7646
7649
|
if (userMessageContent || userMessageAttachments) {
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
role: "user",
|
|
7652
|
-
content: userMessageContent || "",
|
|
7653
|
-
created_at: handoffTimestamp,
|
|
7654
|
-
attachments: userMessageAttachments
|
|
7655
|
-
};
|
|
7656
|
-
await state.storage.sql.exec(
|
|
7657
|
-
`INSERT INTO messages (id, role, content, attachments, created_at, prompt) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
7658
|
-
handoffMessage.id,
|
|
7659
|
-
handoffMessage.role,
|
|
7660
|
-
handoffMessage.content,
|
|
7661
|
-
handoffMessage.attachments || null,
|
|
7662
|
-
handoffMessage.created_at,
|
|
7650
|
+
await this.persistHandoffUserMessage(
|
|
7651
|
+
state,
|
|
7652
|
+
userMessageContent || "",
|
|
7653
|
+
userMessageAttachments,
|
|
7663
7654
|
sideAPrompt.name
|
|
7664
7655
|
);
|
|
7665
|
-
state.messageHistory.push(handoffMessage);
|
|
7666
|
-
if (state.emitMessage) {
|
|
7667
|
-
state.emitMessage(handoffMessage);
|
|
7668
|
-
}
|
|
7669
7656
|
}
|
|
7670
7657
|
state.pendingHandoff = void 0;
|
|
7671
7658
|
state.emitTelemetry?.({
|
|
@@ -7680,6 +7667,37 @@ This usually points to something I can't fix from here \u2014 for example the wo
|
|
|
7680
7667
|
throw error;
|
|
7681
7668
|
}
|
|
7682
7669
|
}
|
|
7670
|
+
/**
|
|
7671
|
+
* Persist the user message that rides an agent handoff, applying the
|
|
7672
|
+
* create-message lifecycle hooks like every other insertion path.
|
|
7673
|
+
*/
|
|
7674
|
+
static async persistHandoffUserMessage(state, content, attachments, promptName) {
|
|
7675
|
+
let handoffMessage = {
|
|
7676
|
+
id: crypto.randomUUID(),
|
|
7677
|
+
role: "user",
|
|
7678
|
+
content,
|
|
7679
|
+
created_at: Date.now() * 1e3,
|
|
7680
|
+
status: "completed",
|
|
7681
|
+
attachments: attachments ?? null
|
|
7682
|
+
};
|
|
7683
|
+
handoffMessage = await this.runBeforeCreateMessageHook(state, handoffMessage);
|
|
7684
|
+
await state.storage.sql.exec(
|
|
7685
|
+
`INSERT INTO messages (id, role, content, attachments, created_at, status, silent, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
7686
|
+
handoffMessage.id,
|
|
7687
|
+
handoffMessage.role,
|
|
7688
|
+
handoffMessage.content,
|
|
7689
|
+
handoffMessage.attachments || null,
|
|
7690
|
+
handoffMessage.created_at,
|
|
7691
|
+
handoffMessage.status ?? "completed",
|
|
7692
|
+
handoffMessage.silent ? 1 : 0,
|
|
7693
|
+
promptName
|
|
7694
|
+
);
|
|
7695
|
+
state.messageHistory.push(handoffMessage);
|
|
7696
|
+
await this.runAfterCreateMessageHook(state, handoffMessage);
|
|
7697
|
+
if (state.emitMessage) {
|
|
7698
|
+
state.emitMessage(handoffMessage);
|
|
7699
|
+
}
|
|
7700
|
+
}
|
|
7683
7701
|
static isEnabledEnvFlag(value) {
|
|
7684
7702
|
if (value === null || value === void 0) return false;
|
|
7685
7703
|
const normalized = value.trim().toLowerCase();
|
|
@@ -8577,7 +8595,16 @@ This usually points to something I can't fix from here \u2014 for example the wo
|
|
|
8577
8595
|
role: "system",
|
|
8578
8596
|
content: systemPromptText
|
|
8579
8597
|
});
|
|
8580
|
-
const
|
|
8598
|
+
const historySnapshot = state.messageHistory.filter(
|
|
8599
|
+
(msg) => msg.status !== "pending"
|
|
8600
|
+
);
|
|
8601
|
+
const filteredHistory = await this.runFilterMessagesHook(
|
|
8602
|
+
state.thread.instance,
|
|
8603
|
+
state,
|
|
8604
|
+
historySnapshot
|
|
8605
|
+
);
|
|
8606
|
+
const contextHistory = this.filterOrphanedToolCalls(filteredHistory);
|
|
8607
|
+
const completedMessages = contextHistory.filter(
|
|
8581
8608
|
(msg) => msg.status !== "pending"
|
|
8582
8609
|
);
|
|
8583
8610
|
const recentMessageThreshold = state.prompt.recentImageThreshold ?? 10;
|
|
@@ -9715,11 +9742,11 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
|
9715
9742
|
* @param message - Message object to be created
|
|
9716
9743
|
* @returns Processed message (or original if hook doesn't exist/fails)
|
|
9717
9744
|
*/
|
|
9718
|
-
static async runBeforeCreateMessageHook(state, message) {
|
|
9745
|
+
static async runBeforeCreateMessageHook(state, message, threadStateOverride, options) {
|
|
9719
9746
|
const executors = await this.getHooksForType(state, "before_create_message");
|
|
9720
9747
|
if (executors.length === 0) return message;
|
|
9721
9748
|
let result = message;
|
|
9722
|
-
const threadState = ThreadStateImpl.fromFlowState(state);
|
|
9749
|
+
const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
|
|
9723
9750
|
for (const executor of executors) {
|
|
9724
9751
|
try {
|
|
9725
9752
|
const hooked = await executor(threadState, _FlowEngine.rowToHookMessage(result));
|
|
@@ -9729,6 +9756,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
|
9729
9756
|
attachments: hooked.attachments === void 0 ? result.attachments : hooked.attachments === null ? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments)
|
|
9730
9757
|
};
|
|
9731
9758
|
} catch (error) {
|
|
9759
|
+
if (options?.propagateHookErrors) {
|
|
9760
|
+
throw error;
|
|
9761
|
+
}
|
|
9732
9762
|
console.error("[Hooks] \u2717 Error running before_create_message hook:", error);
|
|
9733
9763
|
}
|
|
9734
9764
|
}
|
|
@@ -9741,10 +9771,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
|
9741
9771
|
* @param state - Flow state with execution context
|
|
9742
9772
|
* @param message - The created message object
|
|
9743
9773
|
*/
|
|
9744
|
-
static async runAfterCreateMessageHook(state, message) {
|
|
9774
|
+
static async runAfterCreateMessageHook(state, message, threadStateOverride) {
|
|
9745
9775
|
const executors = await this.getHooksForType(state, "after_create_message");
|
|
9746
9776
|
if (executors.length === 0) return;
|
|
9747
|
-
const threadState = ThreadStateImpl.fromFlowState(state);
|
|
9777
|
+
const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
|
|
9748
9778
|
for (const executor of executors) {
|
|
9749
9779
|
try {
|
|
9750
9780
|
await executor(threadState, _FlowEngine.rowToHookMessage(message));
|
|
@@ -10245,7 +10275,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
|
10245
10275
|
}
|
|
10246
10276
|
existingPathMessages.add(content);
|
|
10247
10277
|
const attachmentsJson = JSON.stringify([ref]);
|
|
10248
|
-
|
|
10278
|
+
let silentMessage = {
|
|
10249
10279
|
id: crypto.randomUUID(),
|
|
10250
10280
|
role: sourceMessage.role,
|
|
10251
10281
|
content,
|
|
@@ -10256,6 +10286,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
|
10256
10286
|
parent_id: parentId,
|
|
10257
10287
|
depth
|
|
10258
10288
|
};
|
|
10289
|
+
silentMessage = await this.runBeforeCreateMessageHook(state, silentMessage);
|
|
10259
10290
|
await state.storage.sql.exec(
|
|
10260
10291
|
`
|
|
10261
10292
|
INSERT INTO messages (
|
|
@@ -10266,15 +10297,16 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
|
10266
10297
|
silentMessage.id,
|
|
10267
10298
|
silentMessage.role,
|
|
10268
10299
|
silentMessage.content,
|
|
10269
|
-
|
|
10270
|
-
1,
|
|
10300
|
+
silentMessage.attachments ?? null,
|
|
10301
|
+
silentMessage.silent ? 1 : 0,
|
|
10271
10302
|
silentMessage.created_at,
|
|
10272
|
-
silentMessage.status,
|
|
10303
|
+
silentMessage.status ?? "completed",
|
|
10273
10304
|
silentMessage.parent_id,
|
|
10274
10305
|
silentMessage.depth,
|
|
10275
10306
|
currentPrompt
|
|
10276
10307
|
);
|
|
10277
10308
|
state.messageHistory.push(silentMessage);
|
|
10309
|
+
await this.runAfterCreateMessageHook(state, silentMessage);
|
|
10278
10310
|
if (state.emitMessage) {
|
|
10279
10311
|
state.emitMessage(silentMessage);
|
|
10280
10312
|
}
|
|
@@ -10813,7 +10845,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
|
10813
10845
|
*/
|
|
10814
10846
|
static async loadMessageHistory(state) {
|
|
10815
10847
|
const storage = state.storage;
|
|
10816
|
-
|
|
10848
|
+
state.thread;
|
|
10817
10849
|
const includeParentHistory = state.prompt.include_chat ?? true;
|
|
10818
10850
|
const includeToolCalls = state.prompt.include_past_tools ?? true;
|
|
10819
10851
|
try {
|
|
@@ -10849,8 +10881,11 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
|
10849
10881
|
rows = cursor.toArray();
|
|
10850
10882
|
}
|
|
10851
10883
|
if (state.extraMessages && state.extraMessages.length > 0) {
|
|
10852
|
-
rows.
|
|
10853
|
-
rows.
|
|
10884
|
+
const persistedIds = new Set(rows.map((row) => row.id));
|
|
10885
|
+
rows.push(
|
|
10886
|
+
...state.extraMessages.filter((entry) => !persistedIds.has(entry.id))
|
|
10887
|
+
);
|
|
10888
|
+
rows.sort((a, b) => a.created_at - b.created_at);
|
|
10854
10889
|
}
|
|
10855
10890
|
const messages = rows.map((row) => ({
|
|
10856
10891
|
id: row.id,
|
|
@@ -10873,24 +10908,15 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
|
10873
10908
|
reasoning_content: row.reasoning_content,
|
|
10874
10909
|
reasoning_details: row.reasoning_details
|
|
10875
10910
|
}));
|
|
10876
|
-
return
|
|
10877
|
-
thread.instance,
|
|
10878
|
-
state,
|
|
10879
|
-
this.filterOrphanedToolCalls(messages)
|
|
10880
|
-
);
|
|
10911
|
+
return messages;
|
|
10881
10912
|
} catch (error) {
|
|
10882
10913
|
console.error("Error loading message history:", error);
|
|
10883
10914
|
return [];
|
|
10884
10915
|
}
|
|
10885
10916
|
}
|
|
10886
10917
|
/**
|
|
10887
|
-
*
|
|
10888
|
-
*
|
|
10889
|
-
*
|
|
10890
|
-
* @param thread - Thread instance with hooks registry
|
|
10891
|
-
* @param state - Flow state for hook context
|
|
10892
|
-
* @param rows - SQL rows from messages table
|
|
10893
|
-
* @returns Processed rows (or original if hook doesn't exist/fails)
|
|
10918
|
+
* Build request-context rows from persisted history.
|
|
10919
|
+
* Mutations and synthetic messages stay in the request context.
|
|
10894
10920
|
*/
|
|
10895
10921
|
static async runFilterMessagesHook(thread, state, messages) {
|
|
10896
10922
|
const executors = await this.getHooksForType(state, "filter_messages");
|
|
@@ -10903,13 +10929,56 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
|
10903
10929
|
for (const row of result) if (row.id) byId.set(row.id, row);
|
|
10904
10930
|
const hookView = result.map((row) => _FlowEngine.rowToHookMessage(row));
|
|
10905
10931
|
const kept = await executor(threadState, hookView);
|
|
10906
|
-
result = kept.map((
|
|
10932
|
+
result = kept.map((hookedMessage) => {
|
|
10933
|
+
const original = hookedMessage.id ? byId.get(hookedMessage.id) : void 0;
|
|
10934
|
+
if (original) {
|
|
10935
|
+
return _FlowEngine.applyHookMessageToRow(original, hookedMessage);
|
|
10936
|
+
}
|
|
10937
|
+
return _FlowEngine.hookMessageToContextRow(hookedMessage);
|
|
10938
|
+
});
|
|
10907
10939
|
} catch (error) {
|
|
10908
10940
|
console.error("[Hooks] \u2717 Error running filter_messages hook:", error);
|
|
10909
10941
|
}
|
|
10910
10942
|
}
|
|
10911
10943
|
return result;
|
|
10912
10944
|
}
|
|
10945
|
+
/**
|
|
10946
|
+
* Copy a persisted row and apply hook edits. The original live-history
|
|
10947
|
+
* object is left unchanged.
|
|
10948
|
+
*/
|
|
10949
|
+
static applyHookMessageToRow(original, hooked) {
|
|
10950
|
+
const attachments = hooked.attachments === void 0 ? original.attachments : hooked.attachments === null ? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments);
|
|
10951
|
+
return {
|
|
10952
|
+
...original,
|
|
10953
|
+
role: hooked.role ?? original.role,
|
|
10954
|
+
content: hooked.content !== void 0 ? hooked.content : original.content,
|
|
10955
|
+
name: hooked.name !== void 0 ? hooked.name : original.name,
|
|
10956
|
+
tool_calls: hooked.tool_calls !== void 0 ? hooked.tool_calls : original.tool_calls,
|
|
10957
|
+
tool_call_id: hooked.tool_call_id !== void 0 ? hooked.tool_call_id : original.tool_call_id,
|
|
10958
|
+
silent: hooked.silent !== void 0 ? hooked.silent : original.silent,
|
|
10959
|
+
attachments
|
|
10960
|
+
};
|
|
10961
|
+
}
|
|
10962
|
+
/**
|
|
10963
|
+
* Convert a synthetic hook message into a request-context-only builder row.
|
|
10964
|
+
*/
|
|
10965
|
+
static hookMessageToContextRow(hooked) {
|
|
10966
|
+
const attachments = hooked.attachments === void 0 || hooked.attachments === null ? hooked.attachments ?? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments);
|
|
10967
|
+
return {
|
|
10968
|
+
id: hooked.id || crypto.randomUUID(),
|
|
10969
|
+
role: hooked.role,
|
|
10970
|
+
content: hooked.content ?? null,
|
|
10971
|
+
name: hooked.name ?? null,
|
|
10972
|
+
tool_calls: typeof hooked.tool_calls === "string" ? hooked.tool_calls : null,
|
|
10973
|
+
tool_call_id: hooked.tool_call_id ?? null,
|
|
10974
|
+
created_at: hooked.created_at ?? Date.now() * TIMESTAMP_MULTIPLIER,
|
|
10975
|
+
parent_id: hooked.parent_id ?? null,
|
|
10976
|
+
depth: hooked.depth ?? 0,
|
|
10977
|
+
silent: hooked.silent ?? false,
|
|
10978
|
+
status: "completed",
|
|
10979
|
+
attachments
|
|
10980
|
+
};
|
|
10981
|
+
}
|
|
10913
10982
|
/**
|
|
10914
10983
|
* Log an error to the logs table (best-effort)
|
|
10915
10984
|
* Creates a log entry with error details but no associated message_id
|
|
@@ -11053,6 +11122,35 @@ ${errorStack}` : ""}`;
|
|
|
11053
11122
|
});
|
|
11054
11123
|
|
|
11055
11124
|
// src/agents/utilities.ts
|
|
11125
|
+
var utilities_exports = {};
|
|
11126
|
+
__export(utilities_exports, {
|
|
11127
|
+
cat: () => cat,
|
|
11128
|
+
emitThreadEvent: () => emitThreadEvent,
|
|
11129
|
+
exists: () => exists,
|
|
11130
|
+
find: () => find,
|
|
11131
|
+
forceTurn: () => forceTurn,
|
|
11132
|
+
getFileStats: () => getFileStats,
|
|
11133
|
+
getMessages: () => getMessages,
|
|
11134
|
+
getThumbnail: () => getThumbnail,
|
|
11135
|
+
grep: () => grep,
|
|
11136
|
+
head: () => head,
|
|
11137
|
+
injectMessage: () => injectMessage,
|
|
11138
|
+
linkFile: () => linkFile,
|
|
11139
|
+
mkdir: () => mkdir,
|
|
11140
|
+
move: () => move,
|
|
11141
|
+
queueTool: () => queueTool,
|
|
11142
|
+
readFile: () => readFile,
|
|
11143
|
+
readdir: () => readdir,
|
|
11144
|
+
reloadHistory: () => reloadHistory,
|
|
11145
|
+
rename: () => rename,
|
|
11146
|
+
rmdir: () => rmdir,
|
|
11147
|
+
stat: () => stat,
|
|
11148
|
+
tail: () => tail,
|
|
11149
|
+
unlink: () => unlink,
|
|
11150
|
+
updateThread: () => updateThread,
|
|
11151
|
+
writeFile: () => writeFile,
|
|
11152
|
+
writeImage: () => writeImage
|
|
11153
|
+
});
|
|
11056
11154
|
function queueTool(flow, toolName, args = {}) {
|
|
11057
11155
|
if (flow.currentToolUses !== void 0) {
|
|
11058
11156
|
if (!flow.currentToolUses.includes(toolName)) {
|
|
@@ -11080,7 +11178,7 @@ async function injectMessage(flow, options) {
|
|
|
11080
11178
|
const timestamp = Date.now() * TIMESTAMP_MULTIPLIER;
|
|
11081
11179
|
const silent = options.silent ?? false;
|
|
11082
11180
|
const forceTopLevel = options.forceTopLevel ?? false;
|
|
11083
|
-
|
|
11181
|
+
let message = {
|
|
11084
11182
|
id: messageId,
|
|
11085
11183
|
role: options.role,
|
|
11086
11184
|
content: options.content,
|
|
@@ -11094,41 +11192,52 @@ async function injectMessage(flow, options) {
|
|
|
11094
11192
|
metadata: options.metadata
|
|
11095
11193
|
};
|
|
11096
11194
|
if (options.beforeMessageId) {
|
|
11097
|
-
const
|
|
11098
|
-
|
|
11099
|
-
options.beforeMessageId
|
|
11195
|
+
const liveTarget = flow.messageHistory.find(
|
|
11196
|
+
(entry) => entry.id === options.beforeMessageId
|
|
11100
11197
|
);
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11198
|
+
let targetCreatedAt = liveTarget?.created_at;
|
|
11199
|
+
if (targetCreatedAt === void 0) {
|
|
11200
|
+
const cursor = await flow.storage.sql.exec(
|
|
11201
|
+
`SELECT created_at FROM messages WHERE id = ?`,
|
|
11202
|
+
options.beforeMessageId
|
|
11106
11203
|
);
|
|
11204
|
+
const rows = cursor.toArray();
|
|
11205
|
+
const targetMessage = rows[0];
|
|
11206
|
+
if (!targetMessage) {
|
|
11207
|
+
throw new Error(
|
|
11208
|
+
`Message with id ${options.beforeMessageId} not found`
|
|
11209
|
+
);
|
|
11210
|
+
}
|
|
11211
|
+
targetCreatedAt = targetMessage.created_at;
|
|
11107
11212
|
}
|
|
11108
|
-
message.created_at =
|
|
11213
|
+
message.created_at = targetCreatedAt - 1;
|
|
11109
11214
|
}
|
|
11110
11215
|
const currentPrompt = flow.promptPath.length > 0 ? flow.promptPath[flow.promptPath.length - 1] : null;
|
|
11111
|
-
const
|
|
11216
|
+
const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
|
|
11217
|
+
message = await FlowEngine2.runBeforeCreateMessageHook(flow, message, void 0, {
|
|
11218
|
+
propagateHookErrors: true
|
|
11219
|
+
});
|
|
11220
|
+
const metadataJson = message.metadata === void 0 ? null : JSON.stringify(message.metadata);
|
|
11112
11221
|
await flow.storage.sql.exec(
|
|
11113
11222
|
`INSERT INTO messages (id, role, content, name, tool_call_id, created_at, status, silent, parent_id, depth, metadata, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
11114
|
-
|
|
11223
|
+
message.id,
|
|
11115
11224
|
message.role,
|
|
11116
11225
|
message.content,
|
|
11117
11226
|
message.name,
|
|
11118
11227
|
message.tool_call_id,
|
|
11119
11228
|
message.created_at,
|
|
11120
11229
|
message.status,
|
|
11121
|
-
silent ? 1 : 0,
|
|
11230
|
+
message.silent ? 1 : 0,
|
|
11122
11231
|
message.parent_id,
|
|
11123
11232
|
message.depth,
|
|
11124
11233
|
metadataJson,
|
|
11125
11234
|
currentPrompt
|
|
11126
11235
|
);
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
11130
|
-
flow.messageHistory = await reloadHistory(flow);
|
|
11236
|
+
appendLiveExecutionHistory(flow, message, options.beforeMessageId);
|
|
11237
|
+
if (flow.prompt.include_chat === false || message.parent_id !== null || (message.depth ?? 0) > 0) {
|
|
11238
|
+
appendInjectedContextMessage(flow.extraMessages, message);
|
|
11131
11239
|
}
|
|
11240
|
+
await FlowEngine2.runAfterCreateMessageHook(flow, message);
|
|
11132
11241
|
if (flow.emitMessage) {
|
|
11133
11242
|
flow.emitMessage(message);
|
|
11134
11243
|
}
|
|
@@ -11137,6 +11246,42 @@ async function injectMessage(flow, options) {
|
|
|
11137
11246
|
}
|
|
11138
11247
|
return message;
|
|
11139
11248
|
}
|
|
11249
|
+
function appendLiveExecutionHistory(flow, message, beforeMessageId) {
|
|
11250
|
+
const history = flow.messageHistory;
|
|
11251
|
+
const existing = history.findIndex((entry) => entry.id === message.id);
|
|
11252
|
+
if (existing >= 0) {
|
|
11253
|
+
history.splice(existing, 1);
|
|
11254
|
+
}
|
|
11255
|
+
if (beforeMessageId) {
|
|
11256
|
+
const targetIndex = history.findIndex((entry) => entry.id === beforeMessageId);
|
|
11257
|
+
if (targetIndex >= 0) {
|
|
11258
|
+
history.splice(targetIndex, 0, message);
|
|
11259
|
+
return;
|
|
11260
|
+
}
|
|
11261
|
+
}
|
|
11262
|
+
const insertAt = history.findIndex(
|
|
11263
|
+
(entry) => entry.created_at > message.created_at
|
|
11264
|
+
);
|
|
11265
|
+
if (insertAt === -1) {
|
|
11266
|
+
history.push(message);
|
|
11267
|
+
return;
|
|
11268
|
+
}
|
|
11269
|
+
history.splice(insertAt, 0, message);
|
|
11270
|
+
}
|
|
11271
|
+
function appendInjectedContextMessage(extraMessages, message) {
|
|
11272
|
+
const existing = extraMessages.findIndex((entry) => entry.id === message.id);
|
|
11273
|
+
if (existing >= 0) {
|
|
11274
|
+
extraMessages.splice(existing, 1);
|
|
11275
|
+
}
|
|
11276
|
+
const insertAt = extraMessages.findIndex(
|
|
11277
|
+
(entry) => entry.created_at > message.created_at
|
|
11278
|
+
);
|
|
11279
|
+
if (insertAt === -1) {
|
|
11280
|
+
extraMessages.push(message);
|
|
11281
|
+
return;
|
|
11282
|
+
}
|
|
11283
|
+
extraMessages.splice(insertAt, 0, message);
|
|
11284
|
+
}
|
|
11140
11285
|
async function getMessages(flow, limit = 100, offset = 0, order = "asc") {
|
|
11141
11286
|
const orderClause = order === "desc" ? "DESC" : "ASC";
|
|
11142
11287
|
const cursor = await flow.storage.sql.exec(
|
|
@@ -30030,6 +30175,7 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
30030
30175
|
// src/durable-objects/DurableThread.ts
|
|
30031
30176
|
init_StreamManager();
|
|
30032
30177
|
init_LLMRequest();
|
|
30178
|
+
init_ThreadStateImpl();
|
|
30033
30179
|
|
|
30034
30180
|
// src/durable-objects/logDetailStream.ts
|
|
30035
30181
|
var LOG_DETAIL_HEAVY_FIELDS = [
|
|
@@ -30732,7 +30878,8 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
30732
30878
|
side_b_session_fail_attachments_property: sideBSession.fail.attachmentsProperty,
|
|
30733
30879
|
side_b_session_status_tool: sideBSession.status.toolName,
|
|
30734
30880
|
side_b_session_status_message_property: sideBSession.status.messageProperty,
|
|
30735
|
-
side_b_session_status_attachments_property: sideBSession.status.attachmentsProperty
|
|
30881
|
+
side_b_session_status_attachments_property: sideBSession.status.attachmentsProperty,
|
|
30882
|
+
hooks: Array.isArray(agentDef.hooks) ? agentDef.hooks : void 0
|
|
30736
30883
|
};
|
|
30737
30884
|
}
|
|
30738
30885
|
/**
|
|
@@ -31402,6 +31549,29 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
31402
31549
|
}
|
|
31403
31550
|
return projections;
|
|
31404
31551
|
}
|
|
31552
|
+
/**
|
|
31553
|
+
* Build hook resolution context and a genuine at-rest ThreadState.
|
|
31554
|
+
* Resolution failures abort insertion so lifecycle hooks cannot be bypassed.
|
|
31555
|
+
*/
|
|
31556
|
+
async buildAtRestHookState(threadId) {
|
|
31557
|
+
try {
|
|
31558
|
+
const metadata = await this.getThreadMetadata(threadId);
|
|
31559
|
+
const agentConfig = await this.loadAgentConfig(metadata.agent_id);
|
|
31560
|
+
const threadInstance = this;
|
|
31561
|
+
return {
|
|
31562
|
+
hookState: {
|
|
31563
|
+
agentConfig,
|
|
31564
|
+
thread: { instance: threadInstance, metadata }
|
|
31565
|
+
},
|
|
31566
|
+
threadState: ThreadStateImpl.fromThreadInstance(threadInstance, metadata)
|
|
31567
|
+
};
|
|
31568
|
+
} catch (error) {
|
|
31569
|
+
throw new Error(
|
|
31570
|
+
`Unable to resolve create-message hooks for ${threadId}`,
|
|
31571
|
+
{ cause: error }
|
|
31572
|
+
);
|
|
31573
|
+
}
|
|
31574
|
+
}
|
|
31405
31575
|
/**
|
|
31406
31576
|
* Inject a top-level message without starting a new execution.
|
|
31407
31577
|
*
|
|
@@ -31414,22 +31584,7 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
31414
31584
|
const id = crypto.randomUUID();
|
|
31415
31585
|
const createdAt = Date.now() * TIMESTAMP_MULTIPLIER;
|
|
31416
31586
|
const silent = message.silent === true;
|
|
31417
|
-
|
|
31418
|
-
await this.ctx.storage.sql.exec(
|
|
31419
|
-
`INSERT INTO messages (id, role, content, created_at, status, silent, parent_id, depth, metadata, prompt)
|
|
31420
|
-
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)`,
|
|
31421
|
-
id,
|
|
31422
|
-
message.role,
|
|
31423
|
-
message.content,
|
|
31424
|
-
createdAt,
|
|
31425
|
-
"completed",
|
|
31426
|
-
silent ? 1 : 0,
|
|
31427
|
-
null,
|
|
31428
|
-
0,
|
|
31429
|
-
metadataJson,
|
|
31430
|
-
null
|
|
31431
|
-
);
|
|
31432
|
-
const inserted = {
|
|
31587
|
+
let inserted = {
|
|
31433
31588
|
id,
|
|
31434
31589
|
role: message.role,
|
|
31435
31590
|
content: message.content,
|
|
@@ -31440,9 +31595,37 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
31440
31595
|
depth: 0,
|
|
31441
31596
|
metadata: message.metadata
|
|
31442
31597
|
};
|
|
31598
|
+
const { hookState, threadState } = await this.buildAtRestHookState(threadId);
|
|
31599
|
+
const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
|
|
31600
|
+
inserted = await FlowEngine2.runBeforeCreateMessageHook(
|
|
31601
|
+
hookState,
|
|
31602
|
+
inserted,
|
|
31603
|
+
threadState,
|
|
31604
|
+
{ propagateHookErrors: true }
|
|
31605
|
+
);
|
|
31606
|
+
const metadataJson = inserted.metadata ? JSON.stringify(inserted.metadata) : null;
|
|
31607
|
+
await this.ctx.storage.sql.exec(
|
|
31608
|
+
`INSERT INTO messages (id, role, content, created_at, status, silent, parent_id, depth, metadata, prompt)
|
|
31609
|
+
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)`,
|
|
31610
|
+
inserted.id,
|
|
31611
|
+
inserted.role,
|
|
31612
|
+
inserted.content,
|
|
31613
|
+
inserted.created_at,
|
|
31614
|
+
inserted.status ?? "completed",
|
|
31615
|
+
inserted.silent ? 1 : 0,
|
|
31616
|
+
inserted.parent_id ?? null,
|
|
31617
|
+
inserted.depth ?? 0,
|
|
31618
|
+
metadataJson,
|
|
31619
|
+
null
|
|
31620
|
+
);
|
|
31621
|
+
await FlowEngine2.runAfterCreateMessageHook(
|
|
31622
|
+
hookState,
|
|
31623
|
+
inserted,
|
|
31624
|
+
threadState
|
|
31625
|
+
);
|
|
31443
31626
|
this.broadcastMessage(inserted);
|
|
31444
31627
|
const record = inserted;
|
|
31445
|
-
const visibleUserInjection =
|
|
31628
|
+
const visibleUserInjection = inserted.role === "user" && !inserted.silent && typeof inserted.content === "string" && inserted.content.trim().length > 0 && !isRuntimeSubagentEnvelope(inserted.content);
|
|
31446
31629
|
if (visibleUserInjection || isVisibleTerminalAssistantMessage(record)) {
|
|
31447
31630
|
const stamp = this.recordRegistryActivity(
|
|
31448
31631
|
threadId,
|
|
@@ -37937,8 +38120,7 @@ var DurableUserHub = class extends DurableObject {
|
|
|
37937
38120
|
init_utilities();
|
|
37938
38121
|
|
|
37939
38122
|
// src/agents/FlowStateSdk.ts
|
|
37940
|
-
|
|
37941
|
-
var FlowStateSdk = class _FlowStateSdk {
|
|
38123
|
+
var FlowStateSdk = class {
|
|
37942
38124
|
/**
|
|
37943
38125
|
* Queue a new tool call to be executed in the current flow
|
|
37944
38126
|
*
|
|
@@ -38005,63 +38187,8 @@ var FlowStateSdk = class _FlowStateSdk {
|
|
|
38005
38187
|
* ```
|
|
38006
38188
|
*/
|
|
38007
38189
|
static async injectMessage(flow, options) {
|
|
38008
|
-
const
|
|
38009
|
-
|
|
38010
|
-
const silent = options.silent ?? false;
|
|
38011
|
-
const forceTopLevel = options.forceTopLevel ?? false;
|
|
38012
|
-
const message = {
|
|
38013
|
-
id: messageId,
|
|
38014
|
-
role: options.role,
|
|
38015
|
-
content: options.content,
|
|
38016
|
-
name: options.name ?? null,
|
|
38017
|
-
tool_call_id: options.toolCallId ?? null,
|
|
38018
|
-
created_at: timestamp,
|
|
38019
|
-
status: "completed",
|
|
38020
|
-
silent,
|
|
38021
|
-
parent_id: forceTopLevel ? null : flow.parentMessageId ?? null,
|
|
38022
|
-
depth: forceTopLevel ? 0 : flow.depth
|
|
38023
|
-
};
|
|
38024
|
-
if (options.beforeMessageId) {
|
|
38025
|
-
const cursor = await flow.storage.sql.exec(
|
|
38026
|
-
`SELECT created_at FROM messages WHERE id = ?`,
|
|
38027
|
-
options.beforeMessageId
|
|
38028
|
-
);
|
|
38029
|
-
const rows = cursor.toArray();
|
|
38030
|
-
const targetMessage = rows[0];
|
|
38031
|
-
if (!targetMessage) {
|
|
38032
|
-
throw new Error(
|
|
38033
|
-
`Message with id ${options.beforeMessageId} not found`
|
|
38034
|
-
);
|
|
38035
|
-
}
|
|
38036
|
-
message.created_at = targetMessage.created_at - 1;
|
|
38037
|
-
}
|
|
38038
|
-
const currentPrompt = flow.promptPath.length > 0 ? flow.promptPath[flow.promptPath.length - 1] : null;
|
|
38039
|
-
await flow.storage.sql.exec(
|
|
38040
|
-
`INSERT INTO messages (id, role, content, name, tool_call_id, created_at, status, silent, parent_id, depth, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
38041
|
-
messageId,
|
|
38042
|
-
message.role,
|
|
38043
|
-
message.content,
|
|
38044
|
-
message.name,
|
|
38045
|
-
message.tool_call_id,
|
|
38046
|
-
message.created_at,
|
|
38047
|
-
message.status,
|
|
38048
|
-
silent ? 1 : 0,
|
|
38049
|
-
message.parent_id,
|
|
38050
|
-
message.depth,
|
|
38051
|
-
currentPrompt
|
|
38052
|
-
);
|
|
38053
|
-
if (!silent) {
|
|
38054
|
-
flow.messageHistory.push(message);
|
|
38055
|
-
} else {
|
|
38056
|
-
flow.messageHistory = await _FlowStateSdk.reloadHistory(flow);
|
|
38057
|
-
}
|
|
38058
|
-
if (flow.emitMessage) {
|
|
38059
|
-
flow.emitMessage(message);
|
|
38060
|
-
}
|
|
38061
|
-
if (options.forceTurn) {
|
|
38062
|
-
_FlowStateSdk.forceTurn(flow, options.forceTurn);
|
|
38063
|
-
}
|
|
38064
|
-
return message;
|
|
38190
|
+
const { injectMessage: injectMessage2 } = await Promise.resolve().then(() => (init_utilities(), utilities_exports));
|
|
38191
|
+
return injectMessage2(flow, options);
|
|
38065
38192
|
}
|
|
38066
38193
|
/**
|
|
38067
38194
|
* Get messages from the thread history
|
|
@@ -38150,9 +38277,9 @@ var FlowStateSdk = class _FlowStateSdk {
|
|
|
38150
38277
|
/**
|
|
38151
38278
|
* Reload message history from storage
|
|
38152
38279
|
*
|
|
38153
|
-
* Re-loads the complete message history from the thread's SQLite storage
|
|
38154
|
-
*
|
|
38155
|
-
*
|
|
38280
|
+
* Re-loads the complete message history from the thread's SQLite storage.
|
|
38281
|
+
* This is useful when you need to refresh the message history after making
|
|
38282
|
+
* changes (e.g., injecting messages).
|
|
38156
38283
|
*
|
|
38157
38284
|
* @param flow - The current FlowState
|
|
38158
38285
|
* @returns Array of messages (reloaded from storage)
|