@standardagents/builder 0.38.2 → 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.
@@ -1462,6 +1462,9 @@ function normalizeToolCallMessageSequence(messages) {
1462
1462
  for (const message of messages) {
1463
1463
  const isAssistantToolCallMessage = message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
1464
1464
  if (!pending) {
1465
+ if (message.role === "tool") {
1466
+ continue;
1467
+ }
1465
1468
  if (!isAssistantToolCallMessage) {
1466
1469
  normalized.push(message);
1467
1470
  continue;
@@ -4247,6 +4250,24 @@ var init_ToolExecutor = __esm({
4247
4250
  }
4248
4251
  } catch {
4249
4252
  }
4253
+ const aliasTarget = this.subagentAliasTarget(state, toolName);
4254
+ if (aliasTarget) {
4255
+ if (packageId) {
4256
+ try {
4257
+ const qualified = aliasTarget.includes("/") ? aliasTarget : `${packageId}/${aliasTarget}`;
4258
+ if (await state.thread.instance.loadAgent(qualified)) {
4259
+ return "agent";
4260
+ }
4261
+ } catch {
4262
+ }
4263
+ }
4264
+ try {
4265
+ if (await state.thread.instance.loadAgent(aliasTarget)) {
4266
+ return "agent";
4267
+ }
4268
+ } catch {
4269
+ }
4270
+ }
4250
4271
  const isInToolsArray = (agentName) => {
4251
4272
  const tools2 = state.prompt._tools || [];
4252
4273
  return tools2.some((t) => typeof t === "string" ? t === agentName : t.name === agentName);
@@ -4762,15 +4783,20 @@ Message one with subagent_message(referenceId, message).`
4762
4783
  const builtinCreateAttachmentValue = createArgs?.attachments;
4763
4784
  const builtinCreateInitialArguments = createArgs?.initialArguments ?? {};
4764
4785
  const registryAgentName = targetAgentForCreate || call.function.name;
4786
+ const aliasConfig = this.resolveSubagentToolConfig(state, registryAgentName);
4787
+ const implementingAgentName = typeof aliasConfig.agent === "string" && aliasConfig.agent.trim().length > 0 ? aliasConfig.agent.trim() : registryAgentName;
4765
4788
  const packageId = getPackageId(state);
4766
4789
  let agentDef = null;
4767
- let agentName = registryAgentName;
4790
+ let agentName = implementingAgentName;
4768
4791
  if (builtinSubagentAction === "create" && targetAgentForCreate) {
4769
4792
  const availableSubagents = await this.getPromptSubagentNames(state);
4770
4793
  if (!availableSubagents.includes(targetAgentForCreate)) {
4794
+ const configuredDirect = (state.prompt._tools || []).some(
4795
+ (t) => typeof t === "string" ? t === targetAgentForCreate : t?.name === targetAgentForCreate
4796
+ );
4771
4797
  return {
4772
4798
  status: "error",
4773
- error: `Subagent "${targetAgentForCreate}" is not available for this prompt. Use one of: ${availableSubagents.join(", ") || "(none)"}.`
4799
+ error: configuredDirect ? `"${targetAgentForCreate}" is not a resumable subagent \u2014 call the ${targetAgentForCreate} tool directly; it runs to completion and returns its result in the tool response. subagent_create manages resumable subagents only: ${availableSubagents.join(", ") || "(none)"}.` : `Subagent "${targetAgentForCreate}" is not available for this prompt. Use one of: ${availableSubagents.join(", ") || "(none)"}.`
4774
4800
  };
4775
4801
  }
4776
4802
  }
@@ -4811,7 +4837,7 @@ Message one with subagent_message(referenceId, message).`
4811
4837
  if (agentType === "ai_human") {
4812
4838
  const parentToolConfigs = state.prompt._tools || [];
4813
4839
  const toolConfig = parentToolConfigs.find(
4814
- (t) => typeof t === "string" ? t === agentName : t.name === agentName
4840
+ (t) => typeof t === "string" ? t === registryAgentName || t === agentName : t.name === registryAgentName || t.name === agentName
4815
4841
  );
4816
4842
  const subpromptToolConfig = this.getSubpromptToolConfig(toolConfig);
4817
4843
  const targetState = state.rootState || state;
@@ -5389,6 +5415,27 @@ Message one with subagent_message(referenceId, message).`
5389
5415
  }
5390
5416
  return null;
5391
5417
  }
5418
+ /**
5419
+ * The implementing agent behind an aliased subagent tool name, or null when
5420
+ * the name carries no alias (`agent` absent, empty, or equal to the name).
5421
+ */
5422
+ static subagentAliasTarget(state, toolName) {
5423
+ for (const tool3 of state.prompt._tools || []) {
5424
+ if (!tool3 || typeof tool3 !== "object" || tool3.name !== toolName) {
5425
+ continue;
5426
+ }
5427
+ const target = tool3.agent;
5428
+ if (typeof target === "string" && target.trim().length > 0 && target.trim() !== toolName) {
5429
+ return target.trim();
5430
+ }
5431
+ }
5432
+ return null;
5433
+ }
5434
+ /**
5435
+ * Resolve the prompt's subagent tool config for an EXPOSED tool name.
5436
+ * Config entries are keyed by `name` (the model-facing tool name), which
5437
+ * may alias a differently-named implementing agent via `agent`.
5438
+ */
5392
5439
  static resolveSubagentToolConfig(state, agentName) {
5393
5440
  const config = (state.prompt._tools || []).find(
5394
5441
  (tool3) => typeof tool3 === "string" ? tool3 === agentName : tool3.name === agentName
@@ -7471,28 +7518,12 @@ This usually points to something I can't fix from here \u2014 for example the wo
7471
7518
  state.promptPath = [sideAPrompt.name];
7472
7519
  state.pendingImmediateAfterHandoff = true;
7473
7520
  if (userMessageContent || userMessageAttachments) {
7474
- const handoffMessageId = crypto.randomUUID();
7475
- const handoffTimestamp = Date.now() * 1e3;
7476
- const handoffMessage = {
7477
- id: handoffMessageId,
7478
- role: "user",
7479
- content: userMessageContent || "",
7480
- created_at: handoffTimestamp,
7481
- attachments: userMessageAttachments
7482
- };
7483
- await state.storage.sql.exec(
7484
- `INSERT INTO messages (id, role, content, attachments, created_at, prompt) VALUES (?, ?, ?, ?, ?, ?)`,
7485
- handoffMessage.id,
7486
- handoffMessage.role,
7487
- handoffMessage.content,
7488
- handoffMessage.attachments || null,
7489
- handoffMessage.created_at,
7521
+ await this.persistHandoffUserMessage(
7522
+ state,
7523
+ userMessageContent || "",
7524
+ userMessageAttachments,
7490
7525
  sideAPrompt.name
7491
7526
  );
7492
- state.messageHistory.push(handoffMessage);
7493
- if (state.emitMessage) {
7494
- state.emitMessage(handoffMessage);
7495
- }
7496
7527
  }
7497
7528
  state.pendingHandoff = void 0;
7498
7529
  state.emitTelemetry?.({
@@ -7507,6 +7538,37 @@ This usually points to something I can't fix from here \u2014 for example the wo
7507
7538
  throw error;
7508
7539
  }
7509
7540
  }
7541
+ /**
7542
+ * Persist the user message that rides an agent handoff, applying the
7543
+ * create-message lifecycle hooks like every other insertion path.
7544
+ */
7545
+ static async persistHandoffUserMessage(state, content, attachments, promptName) {
7546
+ let handoffMessage = {
7547
+ id: crypto.randomUUID(),
7548
+ role: "user",
7549
+ content,
7550
+ created_at: Date.now() * 1e3,
7551
+ status: "completed",
7552
+ attachments: attachments ?? null
7553
+ };
7554
+ handoffMessage = await this.runBeforeCreateMessageHook(state, handoffMessage);
7555
+ await state.storage.sql.exec(
7556
+ `INSERT INTO messages (id, role, content, attachments, created_at, status, silent, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
7557
+ handoffMessage.id,
7558
+ handoffMessage.role,
7559
+ handoffMessage.content,
7560
+ handoffMessage.attachments || null,
7561
+ handoffMessage.created_at,
7562
+ handoffMessage.status ?? "completed",
7563
+ handoffMessage.silent ? 1 : 0,
7564
+ promptName
7565
+ );
7566
+ state.messageHistory.push(handoffMessage);
7567
+ await this.runAfterCreateMessageHook(state, handoffMessage);
7568
+ if (state.emitMessage) {
7569
+ state.emitMessage(handoffMessage);
7570
+ }
7571
+ }
7510
7572
  static isEnabledEnvFlag(value) {
7511
7573
  if (value === null || value === void 0) return false;
7512
7574
  const normalized = value.trim().toLowerCase();
@@ -8404,7 +8466,16 @@ This usually points to something I can't fix from here \u2014 for example the wo
8404
8466
  role: "system",
8405
8467
  content: systemPromptText
8406
8468
  });
8407
- const completedMessages = state.messageHistory.filter(
8469
+ const historySnapshot = state.messageHistory.filter(
8470
+ (msg) => msg.status !== "pending"
8471
+ );
8472
+ const filteredHistory = await this.runFilterMessagesHook(
8473
+ state.thread.instance,
8474
+ state,
8475
+ historySnapshot
8476
+ );
8477
+ const contextHistory = this.filterOrphanedToolCalls(filteredHistory);
8478
+ const completedMessages = contextHistory.filter(
8408
8479
  (msg) => msg.status !== "pending"
8409
8480
  );
8410
8481
  const recentMessageThreshold = state.prompt.recentImageThreshold ?? 10;
@@ -8842,7 +8913,8 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
8842
8913
  }
8843
8914
  if (foundToolNames.has(toolName)) continue;
8844
8915
  try {
8845
- const agentDef = await state.thread.instance.loadAgent(toolName);
8916
+ const implementingAgent = subagentConfig && typeof subagentConfig.agent === "string" && subagentConfig.agent.trim().length > 0 ? subagentConfig.agent.trim() : toolName;
8917
+ const agentDef = await state.thread.instance.loadAgent(implementingAgent);
8846
8918
  if (agentDef && (agentDef.type === "ai_human" || agentDef.type === "dual_ai")) {
8847
8919
  let parameters;
8848
8920
  const sideAPromptName = agentDef.sideA?.prompt;
@@ -9056,12 +9128,14 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9056
9128
  if (!toolName || toolName.startsWith("provider:") || seen.has(toolName)) {
9057
9129
  continue;
9058
9130
  }
9059
- const agentDef = await this.tryLoadPromptToolAgent(state, toolName, packageId);
9131
+ const subagentConfigForLoad = this.isSubagentToolConfig(configEntry) ? configEntry : void 0;
9132
+ const implementingAgent = subagentConfigForLoad && typeof subagentConfigForLoad.agent === "string" && subagentConfigForLoad.agent.trim().length > 0 ? subagentConfigForLoad.agent.trim() : toolName;
9133
+ const agentDef = await this.tryLoadPromptToolAgent(state, implementingAgent, packageId);
9060
9134
  if (!agentDef || agentDef.type !== "dual_ai") {
9061
9135
  continue;
9062
9136
  }
9063
9137
  seen.add(toolName);
9064
- const subagentConfig = this.isSubagentToolConfig(configEntry) ? configEntry : void 0;
9138
+ const subagentConfig = subagentConfigForLoad;
9065
9139
  const blocking = subagentConfig?.blocking ?? true;
9066
9140
  const resumable = !!subagentConfig?.resumable && typeof subagentConfig.resumable === "object";
9067
9141
  const receivesMessages = subagentConfig?.resumable && typeof subagentConfig.resumable === "object" ? subagentConfig.resumable.receives_messages : "side_a";
@@ -9539,11 +9613,11 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9539
9613
  * @param message - Message object to be created
9540
9614
  * @returns Processed message (or original if hook doesn't exist/fails)
9541
9615
  */
9542
- static async runBeforeCreateMessageHook(state, message) {
9616
+ static async runBeforeCreateMessageHook(state, message, threadStateOverride, options) {
9543
9617
  const executors = await this.getHooksForType(state, "before_create_message");
9544
9618
  if (executors.length === 0) return message;
9545
9619
  let result = message;
9546
- const threadState = ThreadStateImpl.fromFlowState(state);
9620
+ const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
9547
9621
  for (const executor of executors) {
9548
9622
  try {
9549
9623
  const hooked = await executor(threadState, _FlowEngine.rowToHookMessage(result));
@@ -9553,6 +9627,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9553
9627
  attachments: hooked.attachments === void 0 ? result.attachments : hooked.attachments === null ? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments)
9554
9628
  };
9555
9629
  } catch (error) {
9630
+ if (options?.propagateHookErrors) {
9631
+ throw error;
9632
+ }
9556
9633
  console.error("[Hooks] \u2717 Error running before_create_message hook:", error);
9557
9634
  }
9558
9635
  }
@@ -9565,10 +9642,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9565
9642
  * @param state - Flow state with execution context
9566
9643
  * @param message - The created message object
9567
9644
  */
9568
- static async runAfterCreateMessageHook(state, message) {
9645
+ static async runAfterCreateMessageHook(state, message, threadStateOverride) {
9569
9646
  const executors = await this.getHooksForType(state, "after_create_message");
9570
9647
  if (executors.length === 0) return;
9571
- const threadState = ThreadStateImpl.fromFlowState(state);
9648
+ const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
9572
9649
  for (const executor of executors) {
9573
9650
  try {
9574
9651
  await executor(threadState, _FlowEngine.rowToHookMessage(message));
@@ -10069,7 +10146,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10069
10146
  }
10070
10147
  existingPathMessages.add(content);
10071
10148
  const attachmentsJson = JSON.stringify([ref]);
10072
- const silentMessage = {
10149
+ let silentMessage = {
10073
10150
  id: crypto.randomUUID(),
10074
10151
  role: sourceMessage.role,
10075
10152
  content,
@@ -10080,6 +10157,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10080
10157
  parent_id: parentId,
10081
10158
  depth
10082
10159
  };
10160
+ silentMessage = await this.runBeforeCreateMessageHook(state, silentMessage);
10083
10161
  await state.storage.sql.exec(
10084
10162
  `
10085
10163
  INSERT INTO messages (
@@ -10090,15 +10168,16 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10090
10168
  silentMessage.id,
10091
10169
  silentMessage.role,
10092
10170
  silentMessage.content,
10093
- attachmentsJson,
10094
- 1,
10171
+ silentMessage.attachments ?? null,
10172
+ silentMessage.silent ? 1 : 0,
10095
10173
  silentMessage.created_at,
10096
- silentMessage.status,
10174
+ silentMessage.status ?? "completed",
10097
10175
  silentMessage.parent_id,
10098
10176
  silentMessage.depth,
10099
10177
  currentPrompt
10100
10178
  );
10101
10179
  state.messageHistory.push(silentMessage);
10180
+ await this.runAfterCreateMessageHook(state, silentMessage);
10102
10181
  if (state.emitMessage) {
10103
10182
  state.emitMessage(silentMessage);
10104
10183
  }
@@ -10637,7 +10716,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10637
10716
  */
10638
10717
  static async loadMessageHistory(state) {
10639
10718
  const storage = state.storage;
10640
- const thread = state.thread;
10719
+ state.thread;
10641
10720
  const includeParentHistory = state.prompt.include_chat ?? true;
10642
10721
  const includeToolCalls = state.prompt.include_past_tools ?? true;
10643
10722
  try {
@@ -10673,8 +10752,11 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10673
10752
  rows = cursor.toArray();
10674
10753
  }
10675
10754
  if (state.extraMessages && state.extraMessages.length > 0) {
10676
- rows.push(...state.extraMessages);
10677
- rows.sort((a, b) => a.created_at - b.created_at || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
10755
+ const persistedIds = new Set(rows.map((row) => row.id));
10756
+ rows.push(
10757
+ ...state.extraMessages.filter((entry) => !persistedIds.has(entry.id))
10758
+ );
10759
+ rows.sort((a, b) => a.created_at - b.created_at);
10678
10760
  }
10679
10761
  const messages = rows.map((row) => ({
10680
10762
  id: row.id,
@@ -10697,24 +10779,15 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10697
10779
  reasoning_content: row.reasoning_content,
10698
10780
  reasoning_details: row.reasoning_details
10699
10781
  }));
10700
- return this.runFilterMessagesHook(
10701
- thread.instance,
10702
- state,
10703
- this.filterOrphanedToolCalls(messages)
10704
- );
10782
+ return messages;
10705
10783
  } catch (error) {
10706
10784
  console.error("Error loading message history:", error);
10707
10785
  return [];
10708
10786
  }
10709
10787
  }
10710
10788
  /**
10711
- * Run the filter_messages hook if it exists
10712
- * This hook allows developers to modify SQL row data before transformation
10713
- *
10714
- * @param thread - Thread instance with hooks registry
10715
- * @param state - Flow state for hook context
10716
- * @param rows - SQL rows from messages table
10717
- * @returns Processed rows (or original if hook doesn't exist/fails)
10789
+ * Build request-context rows from persisted history.
10790
+ * Mutations and synthetic messages stay in the request context.
10718
10791
  */
10719
10792
  static async runFilterMessagesHook(thread, state, messages) {
10720
10793
  const executors = await this.getHooksForType(state, "filter_messages");
@@ -10727,13 +10800,56 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10727
10800
  for (const row of result) if (row.id) byId.set(row.id, row);
10728
10801
  const hookView = result.map((row) => _FlowEngine.rowToHookMessage(row));
10729
10802
  const kept = await executor(threadState, hookView);
10730
- result = kept.map((m) => m.id && byId.get(m.id) || null).filter((row) => row !== null);
10803
+ result = kept.map((hookedMessage) => {
10804
+ const original = hookedMessage.id ? byId.get(hookedMessage.id) : void 0;
10805
+ if (original) {
10806
+ return _FlowEngine.applyHookMessageToRow(original, hookedMessage);
10807
+ }
10808
+ return _FlowEngine.hookMessageToContextRow(hookedMessage);
10809
+ });
10731
10810
  } catch (error) {
10732
10811
  console.error("[Hooks] \u2717 Error running filter_messages hook:", error);
10733
10812
  }
10734
10813
  }
10735
10814
  return result;
10736
10815
  }
10816
+ /**
10817
+ * Copy a persisted row and apply hook edits. The original live-history
10818
+ * object is left unchanged.
10819
+ */
10820
+ static applyHookMessageToRow(original, hooked) {
10821
+ const attachments = hooked.attachments === void 0 ? original.attachments : hooked.attachments === null ? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments);
10822
+ return {
10823
+ ...original,
10824
+ role: hooked.role ?? original.role,
10825
+ content: hooked.content !== void 0 ? hooked.content : original.content,
10826
+ name: hooked.name !== void 0 ? hooked.name : original.name,
10827
+ tool_calls: hooked.tool_calls !== void 0 ? hooked.tool_calls : original.tool_calls,
10828
+ tool_call_id: hooked.tool_call_id !== void 0 ? hooked.tool_call_id : original.tool_call_id,
10829
+ silent: hooked.silent !== void 0 ? hooked.silent : original.silent,
10830
+ attachments
10831
+ };
10832
+ }
10833
+ /**
10834
+ * Convert a synthetic hook message into a request-context-only builder row.
10835
+ */
10836
+ static hookMessageToContextRow(hooked) {
10837
+ const attachments = hooked.attachments === void 0 || hooked.attachments === null ? hooked.attachments ?? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments);
10838
+ return {
10839
+ id: hooked.id || crypto.randomUUID(),
10840
+ role: hooked.role,
10841
+ content: hooked.content ?? null,
10842
+ name: hooked.name ?? null,
10843
+ tool_calls: typeof hooked.tool_calls === "string" ? hooked.tool_calls : null,
10844
+ tool_call_id: hooked.tool_call_id ?? null,
10845
+ created_at: hooked.created_at ?? Date.now() * TIMESTAMP_MULTIPLIER,
10846
+ parent_id: hooked.parent_id ?? null,
10847
+ depth: hooked.depth ?? 0,
10848
+ silent: hooked.silent ?? false,
10849
+ status: "completed",
10850
+ attachments
10851
+ };
10852
+ }
10737
10853
  /**
10738
10854
  * Log an error to the logs table (best-effort)
10739
10855
  * Creates a log entry with error details but no associated message_id
@@ -10904,7 +11020,7 @@ async function injectMessage(flow, options) {
10904
11020
  const timestamp = Date.now() * TIMESTAMP_MULTIPLIER;
10905
11021
  const silent = options.silent ?? false;
10906
11022
  const forceTopLevel = options.forceTopLevel ?? false;
10907
- const message = {
11023
+ let message = {
10908
11024
  id: messageId,
10909
11025
  role: options.role,
10910
11026
  content: options.content,
@@ -10918,41 +11034,52 @@ async function injectMessage(flow, options) {
10918
11034
  metadata: options.metadata
10919
11035
  };
10920
11036
  if (options.beforeMessageId) {
10921
- const cursor = await flow.storage.sql.exec(
10922
- `SELECT created_at FROM messages WHERE id = ?`,
10923
- options.beforeMessageId
11037
+ const liveTarget = flow.messageHistory.find(
11038
+ (entry) => entry.id === options.beforeMessageId
10924
11039
  );
10925
- const rows = cursor.toArray();
10926
- const targetMessage = rows[0];
10927
- if (!targetMessage) {
10928
- throw new Error(
10929
- `Message with id ${options.beforeMessageId} not found`
11040
+ let targetCreatedAt = liveTarget?.created_at;
11041
+ if (targetCreatedAt === void 0) {
11042
+ const cursor = await flow.storage.sql.exec(
11043
+ `SELECT created_at FROM messages WHERE id = ?`,
11044
+ options.beforeMessageId
10930
11045
  );
11046
+ const rows = cursor.toArray();
11047
+ const targetMessage = rows[0];
11048
+ if (!targetMessage) {
11049
+ throw new Error(
11050
+ `Message with id ${options.beforeMessageId} not found`
11051
+ );
11052
+ }
11053
+ targetCreatedAt = targetMessage.created_at;
10931
11054
  }
10932
- message.created_at = targetMessage.created_at - 1;
11055
+ message.created_at = targetCreatedAt - 1;
10933
11056
  }
10934
11057
  const currentPrompt = flow.promptPath.length > 0 ? flow.promptPath[flow.promptPath.length - 1] : null;
10935
- const metadataJson = options.metadata ? JSON.stringify(options.metadata) : null;
11058
+ const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
11059
+ message = await FlowEngine2.runBeforeCreateMessageHook(flow, message, void 0, {
11060
+ propagateHookErrors: true
11061
+ });
11062
+ const metadataJson = message.metadata === void 0 ? null : JSON.stringify(message.metadata);
10936
11063
  await flow.storage.sql.exec(
10937
11064
  `INSERT INTO messages (id, role, content, name, tool_call_id, created_at, status, silent, parent_id, depth, metadata, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
10938
- messageId,
11065
+ message.id,
10939
11066
  message.role,
10940
11067
  message.content,
10941
11068
  message.name,
10942
11069
  message.tool_call_id,
10943
11070
  message.created_at,
10944
11071
  message.status,
10945
- silent ? 1 : 0,
11072
+ message.silent ? 1 : 0,
10946
11073
  message.parent_id,
10947
11074
  message.depth,
10948
11075
  metadataJson,
10949
11076
  currentPrompt
10950
11077
  );
10951
- if (!silent) {
10952
- flow.messageHistory.push(message);
10953
- } else {
10954
- flow.messageHistory = await reloadHistory(flow);
11078
+ appendLiveExecutionHistory(flow, message, options.beforeMessageId);
11079
+ if (flow.prompt.include_chat === false || message.parent_id !== null || (message.depth ?? 0) > 0) {
11080
+ appendInjectedContextMessage(flow.extraMessages, message);
10955
11081
  }
11082
+ await FlowEngine2.runAfterCreateMessageHook(flow, message);
10956
11083
  if (flow.emitMessage) {
10957
11084
  flow.emitMessage(message);
10958
11085
  }
@@ -10961,10 +11088,41 @@ async function injectMessage(flow, options) {
10961
11088
  }
10962
11089
  return message;
10963
11090
  }
10964
- async function reloadHistory(state) {
10965
- const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
10966
- state.messageHistory = await FlowEngine2.loadMessageHistory(state);
10967
- return state.messageHistory;
11091
+ function appendLiveExecutionHistory(flow, message, beforeMessageId) {
11092
+ const history = flow.messageHistory;
11093
+ const existing = history.findIndex((entry) => entry.id === message.id);
11094
+ if (existing >= 0) {
11095
+ history.splice(existing, 1);
11096
+ }
11097
+ if (beforeMessageId) {
11098
+ const targetIndex = history.findIndex((entry) => entry.id === beforeMessageId);
11099
+ if (targetIndex >= 0) {
11100
+ history.splice(targetIndex, 0, message);
11101
+ return;
11102
+ }
11103
+ }
11104
+ const insertAt = history.findIndex(
11105
+ (entry) => entry.created_at > message.created_at
11106
+ );
11107
+ if (insertAt === -1) {
11108
+ history.push(message);
11109
+ return;
11110
+ }
11111
+ history.splice(insertAt, 0, message);
11112
+ }
11113
+ function appendInjectedContextMessage(extraMessages, message) {
11114
+ const existing = extraMessages.findIndex((entry) => entry.id === message.id);
11115
+ if (existing >= 0) {
11116
+ extraMessages.splice(existing, 1);
11117
+ }
11118
+ const insertAt = extraMessages.findIndex(
11119
+ (entry) => entry.created_at > message.created_at
11120
+ );
11121
+ if (insertAt === -1) {
11122
+ extraMessages.push(message);
11123
+ return;
11124
+ }
11125
+ extraMessages.splice(insertAt, 0, message);
10968
11126
  }
10969
11127
  function forceTurn(flow, side) {
10970
11128
  if (flow.sequence.isHandling) {
@@ -68323,7 +68481,7 @@ var config_get_default = defineController2(async ({ config, env: env2, req }) =>
68323
68481
  });
68324
68482
 
68325
68483
  // src/version.ts
68326
- var BUILDER_VERSION = "0.38.2".length > 0 ? "0.38.2" : "dev";
68484
+ var BUILDER_VERSION = "0.39.1".length > 0 ? "0.39.1" : "dev";
68327
68485
 
68328
68486
  // src/api/diagnostics.get.ts
68329
68487
  init_LLMRequest();