@standardagents/builder 0.39.0 → 0.40.0

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.
Files changed (42) hide show
  1. package/dist/built-in-routes.js +671 -144
  2. package/dist/built-in-routes.js.map +1 -1
  3. package/dist/client/{ApiKeysView-AH5i-H6j.js → ApiKeysView-DoYvZZ7e.js} +1 -1
  4. package/dist/client/{CenteredContentView-DaBp0K-A.js → CenteredContentView-53PY0cqY.js} +1 -1
  5. package/dist/client/{CompositionView-yY1Q4M23.js → CompositionView-fp-0YnPt.js} +1 -1
  6. package/dist/client/{ConfirmDialog.vue_vue_type_script_setup_true_lang-Bwo5CJzF.js → ConfirmDialog.vue_vue_type_script_setup_true_lang-Cvcagja2.js} +1 -1
  7. package/dist/client/{CopyButton.vue_vue_type_script_setup_true_lang-D0WYwWyA.js → CopyButton.vue_vue_type_script_setup_true_lang-jYvO2v1K.js} +1 -1
  8. package/dist/client/{DataTable.vue_vue_type_script_setup_true_lang-1wwPn2aY.js → DataTable.vue_vue_type_script_setup_true_lang-DQ3Ufa7y.js} +1 -1
  9. package/dist/client/{JsonViewer-Bi8Zdmir.js → JsonViewer-6aaGzutE.js} +1 -1
  10. package/dist/client/{LoginView-BIKahEU6.js → LoginView-DwpCmNw9.js} +1 -1
  11. package/dist/client/{MarketplaceView-DPDPVH68.js → MarketplaceView-Da02L0J5.js} +1 -1
  12. package/dist/client/{Modal.vue_vue_type_script_setup_true_lang-D1_rfOsy.js → Modal.vue_vue_type_script_setup_true_lang-Bi8OjiBf.js} +1 -1
  13. package/dist/client/{ModelModal.vue_vue_type_script_setup_true_lang-DSLeOdxr.js → ModelModal.vue_vue_type_script_setup_true_lang-cYZLOYY7.js} +1 -1
  14. package/dist/client/{ModelsView-Q2WXu1Gw.js → ModelsView-BLlHO9Ud.js} +1 -1
  15. package/dist/client/{PromptEditView-BzA-3n1w.js → PromptEditView-EeNwTDtT.js} +1 -1
  16. package/dist/client/{PromptModal-CJWh_cru.js → PromptModal-BkwrKJ2d.js} +1 -1
  17. package/dist/client/{PromptsView-BezNl-Et.js → PromptsView-D1r7tsLu.js} +1 -1
  18. package/dist/client/{ProvidersView-BuzSKxuT.js → ProvidersView-CmUDl5nM.js} +1 -1
  19. package/dist/client/{ThreadInspectorPane.vue_vue_type_script_setup_true_lang-DOxNp_3C.js → ThreadInspectorPane.vue_vue_type_script_setup_true_lang-cRs3tApQ.js} +1 -1
  20. package/dist/client/{ThreadsView-DPCpZA4J.js → ThreadsView-Bl5fSIO1.js} +1 -1
  21. package/dist/client/{ToolsView-DMjFMAhH.js → ToolsView-DlVEF8-7.js} +1 -1
  22. package/dist/client/{UsersView-B7jXLNQs.js → UsersView-CGlA7a7R.js} +1 -1
  23. package/dist/client/{VariablesView-CF7x0G8g.js → VariablesView-BpYmJL4V.js} +1 -1
  24. package/dist/client/index-CKtIkI0N.js +6 -0
  25. package/dist/client/index.html +1 -1
  26. package/dist/{discovery-DiMJWisl.d.ts → discovery-DZ7fv-b4.d.ts} +3 -2
  27. package/dist/{index-yIcqf7DC.d.ts → index-9MaIifn3.d.ts} +7 -2
  28. package/dist/index.d.ts +4 -4
  29. package/dist/index.js +1170 -563
  30. package/dist/index.js.map +1 -1
  31. package/dist/packing.d.ts +5 -3
  32. package/dist/packing.js +180 -5
  33. package/dist/packing.js.map +1 -1
  34. package/dist/plugin.js +252 -17
  35. package/dist/plugin.js.map +1 -1
  36. package/dist/runtime.d.ts +25 -38
  37. package/dist/runtime.js +1328 -958
  38. package/dist/runtime.js.map +1 -1
  39. package/dist/test.d.ts +2 -2
  40. package/dist/{types-Bpe7IANZ.d.ts → types-BMnBYNwn.d.ts} +3 -1
  41. package/package.json +5 -5
  42. package/dist/client/index-eGECJFYe.js +0 -6
@@ -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;
@@ -6677,6 +6680,255 @@ var init_context = __esm({
6677
6680
  }
6678
6681
  });
6679
6682
 
6683
+ // src/packing/resolver.ts
6684
+ function parseQualifiedName(name15) {
6685
+ if (name15.startsWith("@")) {
6686
+ const firstSlash = name15.indexOf("/");
6687
+ if (firstSlash === -1) {
6688
+ return [null, name15];
6689
+ }
6690
+ const secondSlash = name15.indexOf("/", firstSlash + 1);
6691
+ if (secondSlash === -1) {
6692
+ return [null, name15];
6693
+ }
6694
+ return [name15.substring(0, secondSlash), name15.substring(secondSlash + 1)];
6695
+ }
6696
+ const separatorIndex = name15.indexOf("/");
6697
+ if (separatorIndex === -1) {
6698
+ return [null, name15];
6699
+ }
6700
+ return [name15.substring(0, separatorIndex), name15.substring(separatorIndex + 1)];
6701
+ }
6702
+ function qualifyName(packageId, shortName) {
6703
+ return `${packageId}/${shortName}`;
6704
+ }
6705
+ var init_resolver = __esm({
6706
+ "src/packing/resolver.ts"() {
6707
+ }
6708
+ });
6709
+
6710
+ // src/agents/ParentHookResolver.ts
6711
+ function packageOf(definition) {
6712
+ return definition?.__package ?? definition?._package;
6713
+ }
6714
+ function qualifyWithinPackage(name15, packageId) {
6715
+ if (!packageId || parseQualifiedName(name15)[0] !== null) {
6716
+ return name15;
6717
+ }
6718
+ return qualifyName(packageId, name15);
6719
+ }
6720
+ function promptHooks(prompt) {
6721
+ if (!prompt) return [];
6722
+ return Array.isArray(prompt.hooks) ? prompt.hooks : Array.isArray(prompt._hooks) ? prompt._hooks : [];
6723
+ }
6724
+ function promptTools(prompt) {
6725
+ if (!prompt) return [];
6726
+ return Array.isArray(prompt.tools) ? prompt.tools : Array.isArray(prompt._tools) ? prompt._tools : [];
6727
+ }
6728
+ function relationDescriptor(entry) {
6729
+ if (typeof entry === "string") {
6730
+ return {
6731
+ relationshipName: entry,
6732
+ implementingAgent: entry,
6733
+ explicitlyAgentShaped: false
6734
+ };
6735
+ }
6736
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
6737
+ return null;
6738
+ }
6739
+ const config = entry;
6740
+ if (typeof config.name !== "string" || !config.name) {
6741
+ return null;
6742
+ }
6743
+ const implementingAgent = typeof config.agent === "string" && config.agent ? config.agent : config.name;
6744
+ const explicitlyAgentShaped = "agent" in config || "blocking" in config || "resumable" in config || "immediate" in config || "parentCommunication" in config || "hidden" in config;
6745
+ return {
6746
+ relationshipName: config.name,
6747
+ implementingAgent,
6748
+ optional: typeof config.optional === "string" ? config.optional : void 0,
6749
+ explicitlyAgentShaped
6750
+ };
6751
+ }
6752
+ function hookLoader(host, owner, hookId) {
6753
+ const ownerPackage = packageOf(owner);
6754
+ if (!ownerPackage) {
6755
+ return host.hooks()[hookId];
6756
+ }
6757
+ return host.registry?.()?.packages?.[ownerPackage.packageId]?.hooks?.[hookId];
6758
+ }
6759
+ async function loadBinding(host, owner, hookId, hookType, context, required) {
6760
+ const loader = hookLoader(host, owner, hookId);
6761
+ if (!loader) {
6762
+ if (required) {
6763
+ throw new Error(
6764
+ `Required parent hook '${hookId}' was not found in the child agent namespace`
6765
+ );
6766
+ }
6767
+ return null;
6768
+ }
6769
+ let loaded;
6770
+ try {
6771
+ loaded = await loader();
6772
+ } catch (error) {
6773
+ if (required) throw error;
6774
+ console.error(`[Hooks] Failed to load hook '${hookId}':`, error);
6775
+ return null;
6776
+ }
6777
+ if (!loaded) {
6778
+ if (required) {
6779
+ throw new Error(`Required parent hook '${hookId}' did not export a hook definition`);
6780
+ }
6781
+ return null;
6782
+ }
6783
+ if (typeof loaded.id !== "string" || loaded.id !== hookId) {
6784
+ if (required) {
6785
+ throw new Error(`Required parent hook '${hookId}' exported a mismatched hook ID`);
6786
+ }
6787
+ return null;
6788
+ }
6789
+ if (typeof loaded.execute !== "function") {
6790
+ if (required) {
6791
+ throw new Error(`Required parent hook '${hookId}' did not export an execute function`);
6792
+ }
6793
+ return null;
6794
+ }
6795
+ if (USER_HOOK_NAMES.has(loaded.hook)) {
6796
+ if (required) {
6797
+ throw new Error(
6798
+ `Required parent hook '${hookId}' uses user-scoped hook type '${loaded.hook}'`
6799
+ );
6800
+ }
6801
+ return null;
6802
+ }
6803
+ if (!THREAD_HOOK_NAMES.has(loaded.hook)) {
6804
+ if (required) {
6805
+ throw new Error(`Required parent hook '${hookId}' exported invalid hook type '${loaded.hook}'`);
6806
+ }
6807
+ return null;
6808
+ }
6809
+ if (loaded.hook !== hookType) {
6810
+ return null;
6811
+ }
6812
+ return {
6813
+ id: hookId,
6814
+ hook: hookType,
6815
+ execute: loaded.execute,
6816
+ context
6817
+ };
6818
+ }
6819
+ async function resolveHooksForPrompt(input, hookType) {
6820
+ const agent = input.agent ?? await input.host.loadAgent(input.agentName);
6821
+ const agentPackage = packageOf(agent);
6822
+ const unqualifiedPromptName = input.promptName ?? agent.sideA?.prompt;
6823
+ const resolvedPromptName = unqualifiedPromptName ? qualifyWithinPackage(unqualifiedPromptName, agentPackage?.packageId) : void 0;
6824
+ const prompt = input.prompt ?? (resolvedPromptName ? await input.host.loadPrompt(resolvedPromptName) : void 0);
6825
+ const promptName = prompt?.name ?? prompt?.id ?? resolvedPromptName ?? "unknown_prompt";
6826
+ const before = [];
6827
+ const after = [];
6828
+ for (const entry of promptTools(prompt)) {
6829
+ const relation = relationDescriptor(entry);
6830
+ if (!relation) continue;
6831
+ if (relation.optional && input.isOptionalRelationshipEnabled && !await input.isOptionalRelationshipEnabled(relation.optional)) {
6832
+ continue;
6833
+ }
6834
+ const promptPackage = packageOf(prompt) ?? agentPackage;
6835
+ const childReference = qualifyWithinPackage(
6836
+ relation.implementingAgent,
6837
+ promptPackage?.packageId
6838
+ );
6839
+ let child;
6840
+ try {
6841
+ child = await input.host.loadAgent(childReference);
6842
+ } catch (error) {
6843
+ if (relation.explicitlyAgentShaped) throw error;
6844
+ continue;
6845
+ }
6846
+ if (child.type !== "dual_ai") continue;
6847
+ if (child.parentHooks !== void 0 && child.exposeAsTool !== true) {
6848
+ throw new Error(
6849
+ `Child agent '${childReference}' declares parentHooks but is not exposed as a tool`
6850
+ );
6851
+ }
6852
+ if (child.parentHooks !== void 0 && !Array.isArray(child.parentHooks)) {
6853
+ throw new Error(`Child agent '${childReference}' has an invalid parentHooks declaration`);
6854
+ }
6855
+ const requirements = child.parentHooks ?? [];
6856
+ const seenRequirements = /* @__PURE__ */ new Set();
6857
+ for (const requirement of requirements) {
6858
+ if (!requirement || typeof requirement !== "object" || typeof requirement.hook !== "string" || !requirement.hook || typeof requirement.reason !== "string" || !requirement.reason.trim() || requirement.stage !== void 0 && requirement.stage !== "before_parent" && requirement.stage !== "after_parent" || seenRequirements.has(requirement.hook)) {
6859
+ throw new Error(`Child agent '${childReference}' has an invalid parentHooks requirement`);
6860
+ }
6861
+ seenRequirements.add(requirement.hook);
6862
+ const loaded = await loadBinding(
6863
+ input.host,
6864
+ child,
6865
+ requirement.hook,
6866
+ hookType,
6867
+ {
6868
+ source: {
6869
+ kind: "child",
6870
+ relationshipName: relation.relationshipName,
6871
+ agentName: childReference,
6872
+ package: packageOf(child)
6873
+ }
6874
+ },
6875
+ true
6876
+ );
6877
+ if (!loaded) continue;
6878
+ if ((requirement.stage ?? "after_parent") === "before_parent") {
6879
+ before.push(loaded);
6880
+ } else {
6881
+ after.push(loaded);
6882
+ }
6883
+ }
6884
+ }
6885
+ const promptHookIds = promptHooks(prompt);
6886
+ const promptOwnsBaseHooks = promptHookIds.length > 0 && prompt !== void 0;
6887
+ const baseOwner = promptOwnsBaseHooks ? prompt : agent;
6888
+ const baseIds = promptHookIds.length > 0 ? promptHookIds : Array.isArray(agent.hooks) ? agent.hooks : [];
6889
+ const baseContext = promptOwnsBaseHooks ? { source: { kind: "prompt", promptName } } : { source: { kind: "agent", agentName: input.agentName } };
6890
+ const base = [];
6891
+ for (const hookId of baseIds) {
6892
+ const loaded = await loadBinding(
6893
+ input.host,
6894
+ baseOwner,
6895
+ hookId,
6896
+ hookType,
6897
+ baseContext,
6898
+ false
6899
+ );
6900
+ if (loaded) base.push(loaded);
6901
+ }
6902
+ return [...before, ...base, ...after];
6903
+ }
6904
+ var USER_HOOK_NAMES, THREAD_HOOK_NAMES;
6905
+ var init_ParentHookResolver = __esm({
6906
+ "src/agents/ParentHookResolver.ts"() {
6907
+ init_resolver();
6908
+ USER_HOOK_NAMES = /* @__PURE__ */ new Set([
6909
+ "user_client_attached",
6910
+ "user_client_detached",
6911
+ "user_value_changed"
6912
+ ]);
6913
+ THREAD_HOOK_NAMES = /* @__PURE__ */ new Set([
6914
+ "after_thread_created",
6915
+ "after_subagent_created",
6916
+ "after_system_message",
6917
+ "filter_messages",
6918
+ "prefilter_llm_history",
6919
+ "before_create_message",
6920
+ "after_create_message",
6921
+ "before_update_message",
6922
+ "after_update_message",
6923
+ "before_store_tool_result",
6924
+ "after_tool_call_success",
6925
+ "after_tool_call_failure",
6926
+ "tool_call_started",
6927
+ "tool_call_done"
6928
+ ]);
6929
+ }
6930
+ });
6931
+
6680
6932
  // src/agents/FlowEngine.ts
6681
6933
  var FlowEngine_exports = {};
6682
6934
  __export(FlowEngine_exports, {
@@ -6693,6 +6945,7 @@ var init_FlowEngine = __esm({
6693
6945
  init_context();
6694
6946
  init_sessionTools();
6695
6947
  init_ThreadStateImpl();
6948
+ init_ParentHookResolver();
6696
6949
  IMMEDIATE_BOOTSTRAP_TOOL_NAME = "bootstrap_immediate_subagent";
6697
6950
  HookBlockedExecutionError = class extends Error {
6698
6951
  constructor(message) {
@@ -7035,7 +7288,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
7035
7288
  _variables: def.variables ?? [],
7036
7289
  _env: def.env ?? def.tenvs ?? {},
7037
7290
  _requiredSchema: def.requiredSchema ?? null,
7038
- _hooks: def.hooks ?? []
7291
+ _hooks: def.hooks ?? [],
7292
+ _package: def.__package
7039
7293
  };
7040
7294
  }
7041
7295
  /**
@@ -7425,6 +7679,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
7425
7679
  type: newAgentDef.type,
7426
7680
  created_at: Date.now(),
7427
7681
  max_session_turns: newAgentDef.maxSessionTurns ?? null,
7682
+ hooks: newAgentDef.hooks ?? [],
7683
+ _package: newAgentDef.__package,
7428
7684
  side_a_label: newAgentDef.sideA?.label ?? null,
7429
7685
  side_a_agent_prompt: newAgentDef.sideA?.prompt ?? null,
7430
7686
  side_a_stop_on_response: newAgentDef.sideA?.stopOnResponse ?? true,
@@ -7515,28 +7771,12 @@ This usually points to something I can't fix from here \u2014 for example the wo
7515
7771
  state.promptPath = [sideAPrompt.name];
7516
7772
  state.pendingImmediateAfterHandoff = true;
7517
7773
  if (userMessageContent || userMessageAttachments) {
7518
- const handoffMessageId = crypto.randomUUID();
7519
- const handoffTimestamp = Date.now() * 1e3;
7520
- const handoffMessage = {
7521
- id: handoffMessageId,
7522
- role: "user",
7523
- content: userMessageContent || "",
7524
- created_at: handoffTimestamp,
7525
- attachments: userMessageAttachments
7526
- };
7527
- await state.storage.sql.exec(
7528
- `INSERT INTO messages (id, role, content, attachments, created_at, prompt) VALUES (?, ?, ?, ?, ?, ?)`,
7529
- handoffMessage.id,
7530
- handoffMessage.role,
7531
- handoffMessage.content,
7532
- handoffMessage.attachments || null,
7533
- handoffMessage.created_at,
7774
+ await this.persistHandoffUserMessage(
7775
+ state,
7776
+ userMessageContent || "",
7777
+ userMessageAttachments,
7534
7778
  sideAPrompt.name
7535
7779
  );
7536
- state.messageHistory.push(handoffMessage);
7537
- if (state.emitMessage) {
7538
- state.emitMessage(handoffMessage);
7539
- }
7540
7780
  }
7541
7781
  state.pendingHandoff = void 0;
7542
7782
  state.emitTelemetry?.({
@@ -7551,6 +7791,37 @@ This usually points to something I can't fix from here \u2014 for example the wo
7551
7791
  throw error;
7552
7792
  }
7553
7793
  }
7794
+ /**
7795
+ * Persist the user message that rides an agent handoff, applying the
7796
+ * create-message lifecycle hooks like every other insertion path.
7797
+ */
7798
+ static async persistHandoffUserMessage(state, content, attachments, promptName) {
7799
+ let handoffMessage = {
7800
+ id: crypto.randomUUID(),
7801
+ role: "user",
7802
+ content,
7803
+ created_at: Date.now() * 1e3,
7804
+ status: "completed",
7805
+ attachments: attachments ?? null
7806
+ };
7807
+ handoffMessage = await this.runBeforeCreateMessageHook(state, handoffMessage);
7808
+ await state.storage.sql.exec(
7809
+ `INSERT INTO messages (id, role, content, attachments, created_at, status, silent, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
7810
+ handoffMessage.id,
7811
+ handoffMessage.role,
7812
+ handoffMessage.content,
7813
+ handoffMessage.attachments || null,
7814
+ handoffMessage.created_at,
7815
+ handoffMessage.status ?? "completed",
7816
+ handoffMessage.silent ? 1 : 0,
7817
+ promptName
7818
+ );
7819
+ state.messageHistory.push(handoffMessage);
7820
+ await this.runAfterCreateMessageHook(state, handoffMessage);
7821
+ if (state.emitMessage) {
7822
+ state.emitMessage(handoffMessage);
7823
+ }
7824
+ }
7554
7825
  static isEnabledEnvFlag(value) {
7555
7826
  if (value === null || value === void 0) return false;
7556
7827
  const normalized = value.trim().toLowerCase();
@@ -7570,7 +7841,7 @@ This usually points to something I can't fix from here \u2014 for example the wo
7570
7841
  return null;
7571
7842
  }
7572
7843
  return await stub.resolveThreadEnv({
7573
- threadId: state.threadId,
7844
+ threadId: state.thread.metadata.id,
7574
7845
  property,
7575
7846
  promptName: state.prompt?.name
7576
7847
  });
@@ -8092,8 +8363,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
8092
8363
  state.context[key] = existing;
8093
8364
  }
8094
8365
  static async runImmediateToolsIfNeeded(state) {
8095
- const promptTools = Array.isArray(state.prompt._tools) ? state.prompt._tools : [];
8096
- if (promptTools.length === 0) {
8366
+ const promptTools2 = Array.isArray(state.prompt._tools) ? state.prompt._tools : [];
8367
+ if (promptTools2.length === 0) {
8097
8368
  return false;
8098
8369
  }
8099
8370
  if (!state.immediatePromptRuns) {
@@ -8129,7 +8400,7 @@ This usually points to something I can't fix from here \u2014 for example the wo
8129
8400
  (activeInstanceCounts.get(child.name) ?? 0) + 1
8130
8401
  );
8131
8402
  }
8132
- for (const toolConfig of promptTools) {
8403
+ for (const toolConfig of promptTools2) {
8133
8404
  if (!toolConfig || typeof toolConfig !== "object") {
8134
8405
  continue;
8135
8406
  }
@@ -8295,10 +8566,10 @@ This usually points to something I can't fix from here \u2014 for example the wo
8295
8566
  * model's to clean up, and terminate_subagent on an infrastructure child
8296
8567
  * kills work the runtime depends on. Static and pure for direct testing.
8297
8568
  */
8298
- static filterModelVisibleSubagents(registry, promptTools) {
8569
+ static filterModelVisibleSubagents(registry, promptTools2) {
8299
8570
  if (registry.length === 0) return registry;
8300
8571
  const hidden = /* @__PURE__ */ new Set();
8301
- for (const config of promptTools ?? []) {
8572
+ for (const config of promptTools2 ?? []) {
8302
8573
  if (typeof config === "object" && config !== null && "name" in config && config.hidden === true) {
8303
8574
  hidden.add(config.name);
8304
8575
  }
@@ -8448,7 +8719,16 @@ This usually points to something I can't fix from here \u2014 for example the wo
8448
8719
  role: "system",
8449
8720
  content: systemPromptText
8450
8721
  });
8451
- const completedMessages = state.messageHistory.filter(
8722
+ const historySnapshot = state.messageHistory.filter(
8723
+ (msg) => msg.status !== "pending"
8724
+ );
8725
+ const filteredHistory = await this.runFilterMessagesHook(
8726
+ state.thread.instance,
8727
+ state,
8728
+ historySnapshot
8729
+ );
8730
+ const contextHistory = this.filterOrphanedToolCalls(filteredHistory);
8731
+ const completedMessages = contextHistory.filter(
8452
8732
  (msg) => msg.status !== "pending"
8453
8733
  );
8454
8734
  const recentMessageThreshold = state.prompt.recentImageThreshold ?? 10;
@@ -9319,8 +9599,8 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9319
9599
  try {
9320
9600
  const nativeTools = await this.loadNativeTools(state);
9321
9601
  tools.push(...nativeTools);
9322
- const promptTools = await this.loadPromptTools(state);
9323
- tools.push(...promptTools);
9602
+ const promptTools2 = await this.loadPromptTools(state);
9603
+ tools.push(...promptTools2);
9324
9604
  const agentTools = await this.loadAgentTools(state);
9325
9605
  tools.push(...agentTools);
9326
9606
  const toolNameSources = /* @__PURE__ */ new Map();
@@ -9329,7 +9609,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9329
9609
  sources.add("native tool");
9330
9610
  toolNameSources.set(tool3.function.name, sources);
9331
9611
  }
9332
- for (const tool3 of promptTools) {
9612
+ for (const tool3 of promptTools2) {
9333
9613
  const sources = toolNameSources.get(tool3.function.name) || /* @__PURE__ */ new Set();
9334
9614
  sources.add("prompt");
9335
9615
  toolNameSources.set(tool3.function.name, sources);
@@ -9471,24 +9751,8 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9471
9751
  // Hook Resolution Helpers
9472
9752
  // ==========================================================================
9473
9753
  /**
9474
- * Get the list of hook IDs that should run for the current context.
9475
- * Resolution: prompt hooks > agent hooks > empty array
9476
- *
9477
- * @param state - Flow state with prompt and agent configuration
9478
- * @returns Array of hook IDs to execute
9479
- */
9480
- static getActiveHookIds(state) {
9481
- if (state.prompt?._hooks !== void 0 && state.prompt._hooks.length > 0) {
9482
- return state.prompt._hooks;
9483
- }
9484
- if (state.agentConfig.hooks !== void 0 && state.agentConfig.hooks.length > 0) {
9485
- return state.agentConfig.hooks;
9486
- }
9487
- return [];
9488
- }
9489
- /**
9490
- * Get hooks of a specific type from the active hook IDs.
9491
- * Returns typed hook executors in the order they appear in the hooks array.
9754
+ * Resolve hooks of a specific type for the active prompt, including required
9755
+ * hooks contributed automatically by its enabled direct child agents.
9492
9756
  *
9493
9757
  * @template K - The hook type name (ensures type-safe executors)
9494
9758
  * @param state - Flow state with hook configuration
@@ -9496,24 +9760,17 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9496
9760
  * @returns Array of typed hook executor functions
9497
9761
  */
9498
9762
  static async getHooksForType(state, hookType) {
9499
- const hookIds = this.getActiveHookIds(state);
9500
- if (hookIds.length === 0) return [];
9501
- const hooksRegistry = state.thread.instance.hooks();
9502
- if (!hooksRegistry) return [];
9503
- const executors = [];
9504
- for (const hookId of hookIds) {
9505
- const hookLoader = hooksRegistry[hookId];
9506
- if (!hookLoader) continue;
9507
- try {
9508
- const hookDef = await hookLoader();
9509
- if (hookDef && hookDef.hook === hookType) {
9510
- executors.push(hookDef.execute);
9511
- }
9512
- } catch (error) {
9513
- console.error(`[Hooks] Failed to load hook '${hookId}':`, error);
9514
- }
9515
- }
9516
- return executors;
9763
+ return await resolveHooksForPrompt(
9764
+ {
9765
+ host: state.thread.instance,
9766
+ agentName: state.agentConfig.id,
9767
+ agent: state.agentConfig,
9768
+ promptName: state.prompt?.id,
9769
+ prompt: state.prompt,
9770
+ isOptionalRelationshipEnabled: (envName) => this.isOptionalToolEnabled(state, envName)
9771
+ },
9772
+ hookType
9773
+ );
9517
9774
  }
9518
9775
  // ==========================================================================
9519
9776
  // Hook Runner Methods
@@ -9531,9 +9788,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9531
9788
  if (executors.length === 0) return messages;
9532
9789
  let result = messages;
9533
9790
  const threadState = ThreadStateImpl.fromFlowState(state);
9534
- for (const executor of executors) {
9791
+ for (const { execute, context } of executors) {
9535
9792
  try {
9536
- result = await executor(threadState, result);
9793
+ result = await execute(threadState, result, context);
9537
9794
  } catch (error) {
9538
9795
  if (error && typeof error === "object" && error.block === true) {
9539
9796
  throw new HookBlockedExecutionError(error instanceof Error ? error.message : String(error));
@@ -9551,9 +9808,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9551
9808
  if (executors.length === 0) return systemMessage;
9552
9809
  let result = systemMessage;
9553
9810
  const threadState = ThreadStateImpl.fromFlowState(state);
9554
- for (const executor of executors) {
9811
+ for (const { execute, context } of executors) {
9555
9812
  try {
9556
- const next = await executor(threadState, result);
9813
+ const next = await execute(threadState, result, context);
9557
9814
  if (next !== null && next !== void 0) {
9558
9815
  result = next;
9559
9816
  }
@@ -9570,9 +9827,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9570
9827
  const executors = await this.getHooksForType(state, "after_subagent_created");
9571
9828
  if (executors.length === 0) return;
9572
9829
  const threadState = ThreadStateImpl.fromFlowState(state);
9573
- for (const executor of executors) {
9830
+ for (const { execute, context } of executors) {
9574
9831
  try {
9575
- await executor(threadState, childState);
9832
+ await execute(threadState, childState, context);
9576
9833
  } catch (error) {
9577
9834
  console.error("[Hooks] \u2717 Error running after_subagent_created hook:", error);
9578
9835
  }
@@ -9586,20 +9843,27 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9586
9843
  * @param message - Message object to be created
9587
9844
  * @returns Processed message (or original if hook doesn't exist/fails)
9588
9845
  */
9589
- static async runBeforeCreateMessageHook(state, message) {
9846
+ static async runBeforeCreateMessageHook(state, message, threadStateOverride, options) {
9590
9847
  const executors = await this.getHooksForType(state, "before_create_message");
9591
9848
  if (executors.length === 0) return message;
9592
9849
  let result = message;
9593
- const threadState = ThreadStateImpl.fromFlowState(state);
9594
- for (const executor of executors) {
9850
+ const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
9851
+ for (const { execute, context } of executors) {
9595
9852
  try {
9596
- const hooked = await executor(threadState, _FlowEngine.rowToHookMessage(result));
9853
+ const hooked = await execute(
9854
+ threadState,
9855
+ _FlowEngine.rowToHookMessage(result),
9856
+ context
9857
+ );
9597
9858
  result = {
9598
9859
  ...result,
9599
9860
  ...hooked,
9600
9861
  attachments: hooked.attachments === void 0 ? result.attachments : hooked.attachments === null ? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments)
9601
9862
  };
9602
9863
  } catch (error) {
9864
+ if (options?.propagateHookErrors) {
9865
+ throw error;
9866
+ }
9603
9867
  console.error("[Hooks] \u2717 Error running before_create_message hook:", error);
9604
9868
  }
9605
9869
  }
@@ -9612,13 +9876,13 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9612
9876
  * @param state - Flow state with execution context
9613
9877
  * @param message - The created message object
9614
9878
  */
9615
- static async runAfterCreateMessageHook(state, message) {
9879
+ static async runAfterCreateMessageHook(state, message, threadStateOverride) {
9616
9880
  const executors = await this.getHooksForType(state, "after_create_message");
9617
9881
  if (executors.length === 0) return;
9618
- const threadState = ThreadStateImpl.fromFlowState(state);
9619
- for (const executor of executors) {
9882
+ const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
9883
+ for (const { execute, context } of executors) {
9620
9884
  try {
9621
- await executor(threadState, _FlowEngine.rowToHookMessage(message));
9885
+ await execute(threadState, _FlowEngine.rowToHookMessage(message), context);
9622
9886
  } catch (error) {
9623
9887
  console.error("[Hooks] \u2717 Error running after_create_message hook:", error);
9624
9888
  }
@@ -9659,9 +9923,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9659
9923
  if (executors.length === 0) return updates;
9660
9924
  let result = updates;
9661
9925
  const threadState = ThreadStateImpl.fromFlowState(state);
9662
- for (const executor of executors) {
9926
+ for (const { execute, context } of executors) {
9663
9927
  try {
9664
- result = await executor(threadState, messageId, result);
9928
+ result = await execute(threadState, messageId, result, context);
9665
9929
  } catch (error) {
9666
9930
  console.error("[Hooks] \u2717 Error running before_update_message hook:", error);
9667
9931
  }
@@ -9679,9 +9943,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9679
9943
  const executors = await this.getHooksForType(state, "after_update_message");
9680
9944
  if (executors.length === 0) return;
9681
9945
  const threadState = ThreadStateImpl.fromFlowState(state);
9682
- for (const executor of executors) {
9946
+ for (const { execute, context } of executors) {
9683
9947
  try {
9684
- await executor(threadState, _FlowEngine.rowToHookMessage(message));
9948
+ await execute(threadState, _FlowEngine.rowToHookMessage(message), context);
9685
9949
  } catch (error) {
9686
9950
  console.error("[Hooks] \u2717 Error running after_update_message hook:", error);
9687
9951
  }
@@ -9701,9 +9965,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9701
9965
  if (executors.length === 0) return toolResult;
9702
9966
  let result = toolResult;
9703
9967
  const threadState = ThreadStateImpl.fromFlowState(state);
9704
- for (const executor of executors) {
9968
+ for (const { execute, context } of executors) {
9705
9969
  try {
9706
- result = await executor(threadState, toolCall, result);
9970
+ result = await execute(threadState, toolCall, result, context);
9707
9971
  } catch (error) {
9708
9972
  console.error("[Hooks] \u2717 Error running before_store_tool_result hook:", error);
9709
9973
  }
@@ -9723,10 +9987,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9723
9987
  if (executors.length === 0) return toolResult;
9724
9988
  let result = toolResult;
9725
9989
  const threadState = ThreadStateImpl.fromFlowState(state);
9726
- for (const executor of executors) {
9990
+ for (const { execute, context } of executors) {
9727
9991
  if (result === null) break;
9728
9992
  try {
9729
- result = await executor(threadState, toolCall, result);
9993
+ result = await execute(threadState, toolCall, result, context);
9730
9994
  } catch (error) {
9731
9995
  console.error("[Hooks] \u2717 Error running after_tool_call_success hook:", error);
9732
9996
  }
@@ -9746,10 +10010,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9746
10010
  if (executors.length === 0) return toolResult;
9747
10011
  let result = toolResult;
9748
10012
  const threadState = ThreadStateImpl.fromFlowState(state);
9749
- for (const executor of executors) {
10013
+ for (const { execute, context } of executors) {
9750
10014
  if (result === null) break;
9751
10015
  try {
9752
- result = await executor(threadState, toolCall, result);
10016
+ result = await execute(threadState, toolCall, result, context);
9753
10017
  } catch (error) {
9754
10018
  console.error("[Hooks] \u2717 Error running after_tool_call_failure hook:", error);
9755
10019
  }
@@ -9779,9 +10043,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9779
10043
  const executors = await this.getHooksForType(state, "tool_call_started");
9780
10044
  if (executors.length === 0) return;
9781
10045
  const threadState = ThreadStateImpl.fromFlowState(state);
9782
- for (const executor of executors) {
10046
+ for (const { execute, context } of executors) {
9783
10047
  try {
9784
- await executor(threadState, toolCall, progress);
10048
+ await execute(threadState, toolCall, progress, context);
9785
10049
  } catch (error) {
9786
10050
  console.error("[Hooks] \u2717 Error running tool_call_started hook:", error);
9787
10051
  }
@@ -9809,9 +10073,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9809
10073
  const executors = await this.getHooksForType(state, "tool_call_done");
9810
10074
  if (executors.length === 0) return;
9811
10075
  const threadState = ThreadStateImpl.fromFlowState(state);
9812
- for (const executor of executors) {
10076
+ for (const { execute, context } of executors) {
9813
10077
  try {
9814
- await executor(threadState, toolCall, toolResult);
10078
+ await execute(threadState, toolCall, toolResult, context);
9815
10079
  } catch (error) {
9816
10080
  console.error("[Hooks] \u2717 Error running tool_call_done hook:", error);
9817
10081
  }
@@ -10116,7 +10380,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10116
10380
  }
10117
10381
  existingPathMessages.add(content);
10118
10382
  const attachmentsJson = JSON.stringify([ref]);
10119
- const silentMessage = {
10383
+ let silentMessage = {
10120
10384
  id: crypto.randomUUID(),
10121
10385
  role: sourceMessage.role,
10122
10386
  content,
@@ -10127,6 +10391,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10127
10391
  parent_id: parentId,
10128
10392
  depth
10129
10393
  };
10394
+ silentMessage = await this.runBeforeCreateMessageHook(state, silentMessage);
10130
10395
  await state.storage.sql.exec(
10131
10396
  `
10132
10397
  INSERT INTO messages (
@@ -10137,15 +10402,16 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10137
10402
  silentMessage.id,
10138
10403
  silentMessage.role,
10139
10404
  silentMessage.content,
10140
- attachmentsJson,
10141
- 1,
10405
+ silentMessage.attachments ?? null,
10406
+ silentMessage.silent ? 1 : 0,
10142
10407
  silentMessage.created_at,
10143
- silentMessage.status,
10408
+ silentMessage.status ?? "completed",
10144
10409
  silentMessage.parent_id,
10145
10410
  silentMessage.depth,
10146
10411
  currentPrompt
10147
10412
  );
10148
10413
  state.messageHistory.push(silentMessage);
10414
+ await this.runAfterCreateMessageHook(state, silentMessage);
10149
10415
  if (state.emitMessage) {
10150
10416
  state.emitMessage(silentMessage);
10151
10417
  }
@@ -10684,7 +10950,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10684
10950
  */
10685
10951
  static async loadMessageHistory(state) {
10686
10952
  const storage = state.storage;
10687
- const thread = state.thread;
10953
+ state.thread;
10688
10954
  const includeParentHistory = state.prompt.include_chat ?? true;
10689
10955
  const includeToolCalls = state.prompt.include_past_tools ?? true;
10690
10956
  try {
@@ -10720,8 +10986,11 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10720
10986
  rows = cursor.toArray();
10721
10987
  }
10722
10988
  if (state.extraMessages && state.extraMessages.length > 0) {
10723
- rows.push(...state.extraMessages);
10724
- rows.sort((a, b) => a.created_at - b.created_at || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
10989
+ const persistedIds = new Set(rows.map((row) => row.id));
10990
+ rows.push(
10991
+ ...state.extraMessages.filter((entry) => !persistedIds.has(entry.id))
10992
+ );
10993
+ rows.sort((a, b) => a.created_at - b.created_at);
10725
10994
  }
10726
10995
  const messages = rows.map((row) => ({
10727
10996
  id: row.id,
@@ -10744,43 +11013,77 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10744
11013
  reasoning_content: row.reasoning_content,
10745
11014
  reasoning_details: row.reasoning_details
10746
11015
  }));
10747
- return this.runFilterMessagesHook(
10748
- thread.instance,
10749
- state,
10750
- this.filterOrphanedToolCalls(messages)
10751
- );
11016
+ return messages;
10752
11017
  } catch (error) {
10753
11018
  console.error("Error loading message history:", error);
10754
11019
  return [];
10755
11020
  }
10756
11021
  }
10757
11022
  /**
10758
- * Run the filter_messages hook if it exists
10759
- * This hook allows developers to modify SQL row data before transformation
10760
- *
10761
- * @param thread - Thread instance with hooks registry
10762
- * @param state - Flow state for hook context
10763
- * @param rows - SQL rows from messages table
10764
- * @returns Processed rows (or original if hook doesn't exist/fails)
11023
+ * Build request-context rows from persisted history.
11024
+ * Mutations and synthetic messages stay in the request context.
10765
11025
  */
10766
11026
  static async runFilterMessagesHook(thread, state, messages) {
10767
11027
  const executors = await this.getHooksForType(state, "filter_messages");
10768
11028
  if (executors.length === 0) return messages;
10769
11029
  let result = messages;
10770
11030
  const threadState = ThreadStateImpl.fromFlowState(state);
10771
- for (const executor of executors) {
11031
+ for (const { execute, context } of executors) {
10772
11032
  try {
10773
11033
  const byId = /* @__PURE__ */ new Map();
10774
11034
  for (const row of result) if (row.id) byId.set(row.id, row);
10775
11035
  const hookView = result.map((row) => _FlowEngine.rowToHookMessage(row));
10776
- const kept = await executor(threadState, hookView);
10777
- result = kept.map((m) => m.id && byId.get(m.id) || null).filter((row) => row !== null);
11036
+ const kept = await execute(threadState, hookView, context);
11037
+ result = kept.map((hookedMessage) => {
11038
+ const original = hookedMessage.id ? byId.get(hookedMessage.id) : void 0;
11039
+ if (original) {
11040
+ return _FlowEngine.applyHookMessageToRow(original, hookedMessage);
11041
+ }
11042
+ return _FlowEngine.hookMessageToContextRow(hookedMessage);
11043
+ });
10778
11044
  } catch (error) {
10779
11045
  console.error("[Hooks] \u2717 Error running filter_messages hook:", error);
10780
11046
  }
10781
11047
  }
10782
11048
  return result;
10783
11049
  }
11050
+ /**
11051
+ * Copy a persisted row and apply hook edits. The original live-history
11052
+ * object is left unchanged.
11053
+ */
11054
+ static applyHookMessageToRow(original, hooked) {
11055
+ const attachments = hooked.attachments === void 0 ? original.attachments : hooked.attachments === null ? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments);
11056
+ return {
11057
+ ...original,
11058
+ role: hooked.role ?? original.role,
11059
+ content: hooked.content !== void 0 ? hooked.content : original.content,
11060
+ name: hooked.name !== void 0 ? hooked.name : original.name,
11061
+ tool_calls: hooked.tool_calls !== void 0 ? hooked.tool_calls : original.tool_calls,
11062
+ tool_call_id: hooked.tool_call_id !== void 0 ? hooked.tool_call_id : original.tool_call_id,
11063
+ silent: hooked.silent !== void 0 ? hooked.silent : original.silent,
11064
+ attachments
11065
+ };
11066
+ }
11067
+ /**
11068
+ * Convert a synthetic hook message into a request-context-only builder row.
11069
+ */
11070
+ static hookMessageToContextRow(hooked) {
11071
+ const attachments = hooked.attachments === void 0 || hooked.attachments === null ? hooked.attachments ?? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments);
11072
+ return {
11073
+ id: hooked.id || crypto.randomUUID(),
11074
+ role: hooked.role,
11075
+ content: hooked.content ?? null,
11076
+ name: hooked.name ?? null,
11077
+ tool_calls: typeof hooked.tool_calls === "string" ? hooked.tool_calls : null,
11078
+ tool_call_id: hooked.tool_call_id ?? null,
11079
+ created_at: hooked.created_at ?? Date.now() * TIMESTAMP_MULTIPLIER,
11080
+ parent_id: hooked.parent_id ?? null,
11081
+ depth: hooked.depth ?? 0,
11082
+ silent: hooked.silent ?? false,
11083
+ status: "completed",
11084
+ attachments
11085
+ };
11086
+ }
10784
11087
  /**
10785
11088
  * Log an error to the logs table (best-effort)
10786
11089
  * Creates a log entry with error details but no associated message_id
@@ -10951,7 +11254,7 @@ async function injectMessage(flow, options) {
10951
11254
  const timestamp = Date.now() * TIMESTAMP_MULTIPLIER;
10952
11255
  const silent = options.silent ?? false;
10953
11256
  const forceTopLevel = options.forceTopLevel ?? false;
10954
- const message = {
11257
+ let message = {
10955
11258
  id: messageId,
10956
11259
  role: options.role,
10957
11260
  content: options.content,
@@ -10965,41 +11268,52 @@ async function injectMessage(flow, options) {
10965
11268
  metadata: options.metadata
10966
11269
  };
10967
11270
  if (options.beforeMessageId) {
10968
- const cursor = await flow.storage.sql.exec(
10969
- `SELECT created_at FROM messages WHERE id = ?`,
10970
- options.beforeMessageId
11271
+ const liveTarget = flow.messageHistory.find(
11272
+ (entry) => entry.id === options.beforeMessageId
10971
11273
  );
10972
- const rows = cursor.toArray();
10973
- const targetMessage = rows[0];
10974
- if (!targetMessage) {
10975
- throw new Error(
10976
- `Message with id ${options.beforeMessageId} not found`
11274
+ let targetCreatedAt = liveTarget?.created_at;
11275
+ if (targetCreatedAt === void 0) {
11276
+ const cursor = await flow.storage.sql.exec(
11277
+ `SELECT created_at FROM messages WHERE id = ?`,
11278
+ options.beforeMessageId
10977
11279
  );
11280
+ const rows = cursor.toArray();
11281
+ const targetMessage = rows[0];
11282
+ if (!targetMessage) {
11283
+ throw new Error(
11284
+ `Message with id ${options.beforeMessageId} not found`
11285
+ );
11286
+ }
11287
+ targetCreatedAt = targetMessage.created_at;
10978
11288
  }
10979
- message.created_at = targetMessage.created_at - 1;
11289
+ message.created_at = targetCreatedAt - 1;
10980
11290
  }
10981
11291
  const currentPrompt = flow.promptPath.length > 0 ? flow.promptPath[flow.promptPath.length - 1] : null;
10982
- const metadataJson = options.metadata ? JSON.stringify(options.metadata) : null;
11292
+ const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
11293
+ message = await FlowEngine2.runBeforeCreateMessageHook(flow, message, void 0, {
11294
+ propagateHookErrors: true
11295
+ });
11296
+ const metadataJson = message.metadata === void 0 ? null : JSON.stringify(message.metadata);
10983
11297
  await flow.storage.sql.exec(
10984
11298
  `INSERT INTO messages (id, role, content, name, tool_call_id, created_at, status, silent, parent_id, depth, metadata, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
10985
- messageId,
11299
+ message.id,
10986
11300
  message.role,
10987
11301
  message.content,
10988
11302
  message.name,
10989
11303
  message.tool_call_id,
10990
11304
  message.created_at,
10991
11305
  message.status,
10992
- silent ? 1 : 0,
11306
+ message.silent ? 1 : 0,
10993
11307
  message.parent_id,
10994
11308
  message.depth,
10995
11309
  metadataJson,
10996
11310
  currentPrompt
10997
11311
  );
10998
- if (!silent) {
10999
- flow.messageHistory.push(message);
11000
- } else {
11001
- flow.messageHistory = await reloadHistory(flow);
11312
+ appendLiveExecutionHistory(flow, message, options.beforeMessageId);
11313
+ if (flow.prompt.include_chat === false || message.parent_id !== null || (message.depth ?? 0) > 0) {
11314
+ appendInjectedContextMessage(flow.extraMessages, message);
11002
11315
  }
11316
+ await FlowEngine2.runAfterCreateMessageHook(flow, message);
11003
11317
  if (flow.emitMessage) {
11004
11318
  flow.emitMessage(message);
11005
11319
  }
@@ -11008,10 +11322,41 @@ async function injectMessage(flow, options) {
11008
11322
  }
11009
11323
  return message;
11010
11324
  }
11011
- async function reloadHistory(state) {
11012
- const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
11013
- state.messageHistory = await FlowEngine2.loadMessageHistory(state);
11014
- return state.messageHistory;
11325
+ function appendLiveExecutionHistory(flow, message, beforeMessageId) {
11326
+ const history = flow.messageHistory;
11327
+ const existing = history.findIndex((entry) => entry.id === message.id);
11328
+ if (existing >= 0) {
11329
+ history.splice(existing, 1);
11330
+ }
11331
+ if (beforeMessageId) {
11332
+ const targetIndex = history.findIndex((entry) => entry.id === beforeMessageId);
11333
+ if (targetIndex >= 0) {
11334
+ history.splice(targetIndex, 0, message);
11335
+ return;
11336
+ }
11337
+ }
11338
+ const insertAt = history.findIndex(
11339
+ (entry) => entry.created_at > message.created_at
11340
+ );
11341
+ if (insertAt === -1) {
11342
+ history.push(message);
11343
+ return;
11344
+ }
11345
+ history.splice(insertAt, 0, message);
11346
+ }
11347
+ function appendInjectedContextMessage(extraMessages, message) {
11348
+ const existing = extraMessages.findIndex((entry) => entry.id === message.id);
11349
+ if (existing >= 0) {
11350
+ extraMessages.splice(existing, 1);
11351
+ }
11352
+ const insertAt = extraMessages.findIndex(
11353
+ (entry) => entry.created_at > message.created_at
11354
+ );
11355
+ if (insertAt === -1) {
11356
+ extraMessages.push(message);
11357
+ return;
11358
+ }
11359
+ extraMessages.splice(insertAt, 0, message);
11015
11360
  }
11016
11361
  function forceTurn(flow, side) {
11017
11362
  if (flow.sequence.isHandling) {
@@ -14692,11 +15037,19 @@ async function extractPromptTools(sourceCode) {
14692
15037
  result.tools.push(element.text);
14693
15038
  } else if (ts.isObjectLiteralExpression(element)) {
14694
15039
  result.hasSubpromptConfigs = true;
15040
+ let configuredName;
15041
+ let implementingAgent;
14695
15042
  for (const objProp of element.properties) {
14696
- if (ts.isPropertyAssignment(objProp) && ts.isIdentifier(objProp.name) && objProp.name.text === "name" && ts.isStringLiteral(objProp.initializer)) {
14697
- result.tools.push(objProp.initializer.text);
15043
+ if (ts.isPropertyAssignment(objProp) && ts.isIdentifier(objProp.name) && ts.isStringLiteral(objProp.initializer)) {
15044
+ if (objProp.name.text === "name") {
15045
+ configuredName = objProp.initializer.text;
15046
+ } else if (objProp.name.text === "agent") {
15047
+ implementingAgent = objProp.initializer.text;
15048
+ }
14698
15049
  }
14699
15050
  }
15051
+ const dependencyName = implementingAgent ?? configuredName;
15052
+ if (dependencyName) result.tools.push(dependencyName);
14700
15053
  }
14701
15054
  }
14702
15055
  }
@@ -14819,6 +15172,106 @@ async function extractAgentPrompts(sourceCode) {
14819
15172
  visit2(sourceFile);
14820
15173
  return result;
14821
15174
  }
15175
+ async function extractAgentHooks(sourceCode) {
15176
+ const ts = await loadTypeScript();
15177
+ const sourceFile = ts.createSourceFile(
15178
+ "agent.ts",
15179
+ sourceCode,
15180
+ ts.ScriptTarget.ESNext,
15181
+ true
15182
+ );
15183
+ const arrays = /* @__PURE__ */ new Map();
15184
+ const result = {
15185
+ hooks: [],
15186
+ parentHooks: [],
15187
+ parentHookRequirements: []
15188
+ };
15189
+ const unwrapArray = (expression) => {
15190
+ let current = expression;
15191
+ while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isSatisfiesExpression(current)) {
15192
+ current = current.expression;
15193
+ }
15194
+ if (ts.isArrayLiteralExpression(current)) return current;
15195
+ if (ts.isIdentifier(current)) return arrays.get(current.text) ?? null;
15196
+ return null;
15197
+ };
15198
+ for (const statement of sourceFile.statements) {
15199
+ if (!ts.isVariableStatement(statement)) continue;
15200
+ for (const declaration of statement.declarationList.declarations) {
15201
+ if (ts.isIdentifier(declaration.name) && declaration.initializer) {
15202
+ const array44 = unwrapArray(declaration.initializer);
15203
+ if (array44) arrays.set(declaration.name.text, array44);
15204
+ }
15205
+ }
15206
+ }
15207
+ const pushUnique = (target, value) => {
15208
+ if (!target.includes(value)) target.push(value);
15209
+ };
15210
+ function visit2(node) {
15211
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "defineAgent" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
15212
+ for (const property of node.arguments[0].properties) {
15213
+ if (!ts.isPropertyAssignment(property) || !ts.isIdentifier(property.name)) continue;
15214
+ const array44 = unwrapArray(property.initializer);
15215
+ if (!array44) continue;
15216
+ if (property.name.text === "hooks") {
15217
+ for (const element of array44.elements) {
15218
+ if (ts.isStringLiteral(element)) pushUnique(result.hooks, element.text);
15219
+ }
15220
+ }
15221
+ if (property.name.text === "parentHooks") {
15222
+ for (const element of array44.elements) {
15223
+ if (!ts.isObjectLiteralExpression(element)) continue;
15224
+ let hook;
15225
+ let reason;
15226
+ let stage;
15227
+ for (const requirement of element.properties) {
15228
+ if (ts.isPropertyAssignment(requirement) && ts.isIdentifier(requirement.name) && ts.isStringLiteral(requirement.initializer)) {
15229
+ if (requirement.name.text === "hook") hook = requirement.initializer.text;
15230
+ if (requirement.name.text === "reason") reason = requirement.initializer.text;
15231
+ if (requirement.name.text === "stage" && (requirement.initializer.text === "before_parent" || requirement.initializer.text === "after_parent")) {
15232
+ stage = requirement.initializer.text;
15233
+ }
15234
+ }
15235
+ }
15236
+ if (hook) {
15237
+ pushUnique(result.parentHooks, hook);
15238
+ if (reason) result.parentHookRequirements.push({ hook, reason, stage });
15239
+ }
15240
+ }
15241
+ }
15242
+ }
15243
+ }
15244
+ ts.forEachChild(node, visit2);
15245
+ }
15246
+ visit2(sourceFile);
15247
+ return result;
15248
+ }
15249
+ async function extractPromptHooks(sourceCode) {
15250
+ const ts = await loadTypeScript();
15251
+ const sourceFile = ts.createSourceFile(
15252
+ "prompt.ts",
15253
+ sourceCode,
15254
+ ts.ScriptTarget.ESNext,
15255
+ true
15256
+ );
15257
+ const hooks = [];
15258
+ function visit2(node) {
15259
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "definePrompt" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
15260
+ for (const property of node.arguments[0].properties) {
15261
+ if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.text === "hooks" && ts.isArrayLiteralExpression(property.initializer)) {
15262
+ for (const element of property.initializer.elements) {
15263
+ if (ts.isStringLiteral(element) && !hooks.includes(element.text)) {
15264
+ hooks.push(element.text);
15265
+ }
15266
+ }
15267
+ }
15268
+ }
15269
+ }
15270
+ ts.forEachChild(node, visit2);
15271
+ }
15272
+ visit2(sourceFile);
15273
+ return hooks;
15274
+ }
14822
15275
  async function extractAgentDescription(sourceCode) {
14823
15276
  const ts = await loadTypeScript();
14824
15277
  const sourceFile = ts.createSourceFile(
@@ -15074,6 +15527,13 @@ var init_packing_service = __esm({
15074
15527
  });
15075
15528
  const agentSource = fs4.readFileSync(agentFilePath, "utf-8");
15076
15529
  const agentPrompts = await extractAgentPrompts(agentSource);
15530
+ const agentHooks = await extractAgentHooks(agentSource);
15531
+ for (const hookId of agentHooks.hooks) {
15532
+ await this.analyzeHook(hookId, agentsDir, analysis, false, `agent:${agentName}`);
15533
+ }
15534
+ for (const hookId of agentHooks.parentHooks) {
15535
+ await this.analyzeHook(hookId, agentsDir, analysis, true, `agent:${agentName}`);
15536
+ }
15077
15537
  if (agentPrompts.sideA) {
15078
15538
  analysis.primaryPrompt = agentPrompts.sideA;
15079
15539
  await this.analyzePrompt(agentPrompts.sideA, agentsDir, analysis, /* @__PURE__ */ new Set(), `agent:${agentName}`);
@@ -15276,6 +15736,9 @@ var init_packing_service = __esm({
15276
15736
  }
15277
15737
  visited.add(`prompt:${promptName}`);
15278
15738
  const promptSource = fs4.readFileSync(promptFilePath, "utf-8");
15739
+ for (const hookId of await extractPromptHooks(promptSource)) {
15740
+ await this.analyzeHook(hookId, agentsDir, analysis, false, thisKey);
15741
+ }
15279
15742
  const modelName = await extractPromptModel(promptSource);
15280
15743
  if (modelName) {
15281
15744
  await this.analyzeModel(modelName, agentsDir, analysis, visited, thisKey);
@@ -15360,6 +15823,13 @@ var init_packing_service = __esm({
15360
15823
  visited.add(`agent:${agentName}`);
15361
15824
  const agentSource = fs4.readFileSync(agentFilePath, "utf-8");
15362
15825
  const agentPrompts = await extractAgentPrompts(agentSource);
15826
+ const agentHooks = await extractAgentHooks(agentSource);
15827
+ for (const hookId of agentHooks.hooks) {
15828
+ await this.analyzeHook(hookId, agentsDir, analysis, false, thisKey);
15829
+ }
15830
+ for (const hookId of agentHooks.parentHooks) {
15831
+ await this.analyzeHook(hookId, agentsDir, analysis, true, thisKey);
15832
+ }
15363
15833
  if (agentPrompts.sideA) {
15364
15834
  await this.analyzePrompt(agentPrompts.sideA, agentsDir, analysis, visited, thisKey);
15365
15835
  }
@@ -15367,6 +15837,27 @@ var init_packing_service = __esm({
15367
15837
  await this.analyzePrompt(agentPrompts.sideB, agentsDir, analysis, visited, thisKey);
15368
15838
  }
15369
15839
  }
15840
+ /** Add a hook referenced by an agent or prompt to the packed graph. */
15841
+ async analyzeHook(hookId, agentsDir, analysis, required, parentKey) {
15842
+ const hookFilePath = await this.findFile(path4.join(agentsDir, "hooks"), hookId);
15843
+ if (!hookFilePath) {
15844
+ const message = `Hook file not found for ID: ${hookId}`;
15845
+ if (required) analysis.errors.push(message);
15846
+ else analysis.warnings.push(message);
15847
+ return;
15848
+ }
15849
+ if (!analysis.constituents.hooks.some(
15850
+ (hook) => hook.name === hookId && hook.parentKey === parentKey
15851
+ )) {
15852
+ analysis.constituents.hooks.push({
15853
+ name: hookId,
15854
+ filePath: hookFilePath,
15855
+ discoveredVia: "static",
15856
+ sharedWith: [],
15857
+ parentKey
15858
+ });
15859
+ }
15860
+ }
15370
15861
  /**
15371
15862
  * Analyze a model and its fallbacks.
15372
15863
  */
@@ -15501,11 +15992,17 @@ var init_packing_service = __esm({
15501
15992
  } = options;
15502
15993
  const finalPackageName = packageName || packageId;
15503
15994
  const packedAt = Date.now();
15995
+ const entryAgentFile = await this.findFile(
15996
+ path4.join(rootDir, "agents", "agents"),
15997
+ agentName
15998
+ );
15999
+ const entryParentHooks = entryAgentFile ? (await extractAgentHooks(fs4.readFileSync(entryAgentFile, "utf-8"))).parentHookRequirements : [];
15504
16000
  const meta = {
15505
16001
  packageId,
15506
16002
  version,
15507
16003
  entryAgents: [agentName],
15508
- packedAt
16004
+ packedAt,
16005
+ ...entryParentHooks.length > 0 ? { parentHooks: { [agentName]: entryParentHooks } } : {}
15509
16006
  };
15510
16007
  const result = {
15511
16008
  success: false,
@@ -15614,6 +16111,7 @@ var init_packing_service = __esm({
15614
16111
  agentName,
15615
16112
  this.deduplicateConstituents(analysis).effects.map((effect) => effect.name),
15616
16113
  this.deduplicateConstituents(analysis).threadEndpoints.map((endpoint) => endpoint.name),
16114
+ meta.parentHooks,
15617
16115
  externalDeps,
15618
16116
  rootDir,
15619
16117
  license,
@@ -16005,7 +16503,7 @@ var init_packing_service = __esm({
16005
16503
  /**
16006
16504
  * Generate the package.json file with dependencies.
16007
16505
  */
16008
- generatePackageJson(packageName, version, agentName, effects, threadEndpoints, externalDeps, rootDir, license, licenseOwner) {
16506
+ generatePackageJson(packageName, version, agentName, effects, threadEndpoints, parentHooks, externalDeps, rootDir, license, licenseOwner) {
16009
16507
  const dependencies = {
16010
16508
  "@standardagents/spec": this.resolvePackageVersion("@standardagents/spec", rootDir)
16011
16509
  };
@@ -16027,6 +16525,7 @@ var init_packing_service = __esm({
16027
16525
  keywords: ["standardagent"],
16028
16526
  standardagent: {
16029
16527
  entryAgents: [agentName],
16528
+ ...parentHooks ? { parentHooks } : {},
16030
16529
  ...effects.length > 0 ? { effects } : {},
16031
16530
  ...threadEndpoints.length > 0 ? { threadEndpoints } : {}
16032
16531
  },
@@ -16322,6 +16821,22 @@ ${license || "See LICENSE file"}
16322
16821
  };
16323
16822
  }
16324
16823
  });
16824
+ function validParentHooks(value) {
16825
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
16826
+ return Object.values(value).every((requirements) => {
16827
+ if (!Array.isArray(requirements)) return false;
16828
+ const seen = /* @__PURE__ */ new Set();
16829
+ return requirements.every((requirement) => {
16830
+ if (typeof requirement !== "object" || requirement === null) return false;
16831
+ const item = requirement;
16832
+ if (typeof item.hook !== "string" || !item.hook.trim() || typeof item.reason !== "string" || !item.reason.trim() || item.stage !== void 0 && item.stage !== "before_parent" && item.stage !== "after_parent" || seen.has(item.hook)) {
16833
+ return false;
16834
+ }
16835
+ seen.add(item.hook);
16836
+ return true;
16837
+ });
16838
+ });
16839
+ }
16325
16840
  var PackageDiscoveryService;
16326
16841
  var init_discovery = __esm({
16327
16842
  "src/packing/discovery.ts"() {
@@ -16420,6 +16935,7 @@ var init_discovery = __esm({
16420
16935
  }
16421
16936
  const effects = Array.isArray(pkgJson.standardagent.effects) && pkgJson.standardagent.effects.every((effectName) => typeof effectName === "string") ? pkgJson.standardagent.effects : void 0;
16422
16937
  const threadEndpoints = Array.isArray(pkgJson.standardagent.threadEndpoints) && pkgJson.standardagent.threadEndpoints.every((endpoint) => typeof endpoint === "string") ? pkgJson.standardagent.threadEndpoints : void 0;
16938
+ const parentHooks = validParentHooks(pkgJson.standardagent.parentHooks) ? pkgJson.standardagent.parentHooks : void 0;
16423
16939
  return {
16424
16940
  packageId: pkgJson.name || pkgName,
16425
16941
  version: pkgJson.version || "0.0.0",
@@ -16428,6 +16944,7 @@ var init_discovery = __esm({
16428
16944
  entryAgents: pkgJson.standardagent.entryAgents,
16429
16945
  ...effects ? { effects } : {},
16430
16946
  ...threadEndpoints ? { threadEndpoints } : {},
16947
+ ...parentHooks ? { parentHooks } : {},
16431
16948
  description: pkgJson.description
16432
16949
  };
16433
16950
  } catch (error) {
@@ -16489,6 +17006,7 @@ var init_discovery = __esm({
16489
17006
  }
16490
17007
  const effects = Array.isArray(pkgJson.standardagent.effects) && pkgJson.standardagent.effects.every((effectName) => typeof effectName === "string") ? pkgJson.standardagent.effects : void 0;
16491
17008
  const threadEndpoints = Array.isArray(pkgJson.standardagent.threadEndpoints) && pkgJson.standardagent.threadEndpoints.every((endpoint) => typeof endpoint === "string") ? pkgJson.standardagent.threadEndpoints : void 0;
17009
+ const parentHooks = validParentHooks(pkgJson.standardagent.parentHooks) ? pkgJson.standardagent.parentHooks : void 0;
16492
17010
  return {
16493
17011
  packageId: pkgJson.name || dirName,
16494
17012
  version: pkgJson.version || "0.0.0",
@@ -16497,6 +17015,7 @@ var init_discovery = __esm({
16497
17015
  entryAgents: pkgJson.standardagent.entryAgents,
16498
17016
  ...effects ? { effects } : {},
16499
17017
  ...threadEndpoints ? { threadEndpoints } : {},
17018
+ ...parentHooks ? { parentHooks } : {},
16500
17019
  description: pkgJson.description
16501
17020
  };
16502
17021
  } catch (error) {
@@ -16525,7 +17044,8 @@ var init_discovery = __esm({
16525
17044
  const hasValidEntryAgents = Array.isArray(f.entryAgents) && f.entryAgents.length > 0 && f.entryAgents.every((e) => typeof e === "string");
16526
17045
  const hasValidEffects = f.effects === void 0 || Array.isArray(f.effects) && f.effects.every((effectName) => typeof effectName === "string");
16527
17046
  const hasValidThreadEndpoints = f.threadEndpoints === void 0 || Array.isArray(f.threadEndpoints) && f.threadEndpoints.every((endpoint) => typeof endpoint === "string");
16528
- return hasValidEntryAgents && hasValidEffects && hasValidThreadEndpoints;
17047
+ const hasValidParentHooks = f.parentHooks === void 0 || validParentHooks(f.parentHooks);
17048
+ return hasValidEntryAgents && hasValidEffects && hasValidThreadEndpoints && hasValidParentHooks;
16529
17049
  }
16530
17050
  /**
16531
17051
  * Get a package by ID.
@@ -67824,8 +68344,10 @@ var agents_get_default = defineController2(async ({ agents, agentNames, registry
67824
68344
  title: definition.title || definition.name,
67825
68345
  type: definition.type || "ai_human",
67826
68346
  description: definition.description || null,
68347
+ tool_description: definition.toolDescription || null,
67827
68348
  icon: definition.icon || null,
67828
68349
  primary: definition.primary !== false,
68350
+ parentHooks: definition.parentHooks || [],
67829
68351
  ...includeAuthoringDetails ? {
67830
68352
  default_prompt: definition.sideA?.prompt || "",
67831
68353
  default_model: "",
@@ -67861,8 +68383,10 @@ var agents_get_default = defineController2(async ({ agents, agentNames, registry
67861
68383
  title: definition.title || definition.name,
67862
68384
  type: definition.type || "ai_human",
67863
68385
  description: definition.description || null,
68386
+ tool_description: definition.toolDescription || null,
67864
68387
  icon: definition.icon || null,
67865
68388
  primary: definition.primary !== false,
68389
+ parentHooks: definition.parentHooks || [],
67866
68390
  ...includeAuthoringDetails ? {
67867
68391
  default_prompt: definition.sideA?.prompt || "",
67868
68392
  default_model: "",
@@ -68370,7 +68894,7 @@ var config_get_default = defineController2(async ({ config, env: env2, req }) =>
68370
68894
  });
68371
68895
 
68372
68896
  // src/version.ts
68373
- var BUILDER_VERSION = "0.39.0".length > 0 ? "0.39.0" : "dev";
68897
+ var BUILDER_VERSION = "0.40.0".length > 0 ? "0.40.0" : "dev";
68374
68898
 
68375
68899
  // src/api/diagnostics.get.ts
68376
68900
  init_LLMRequest();
@@ -69743,8 +70267,10 @@ migrations.reduce(
69743
70267
  );
69744
70268
 
69745
70269
  // src/durable-objects/DurableAgentBuilder.ts
70270
+ init_resolver();
69746
70271
  init_types();
69747
70272
  init_scope();
70273
+ init_ParentHookResolver();
69748
70274
  var THREAD_PREVIEW_MAX_CHARS = 140;
69749
70275
  var buildThreadPreview = (content) => {
69750
70276
  const collapsed = content.replace(/\s+/g, " ").trim();
@@ -70271,6 +70797,7 @@ var name_get_default = defineController2(async ({ params, agents, prompts, promp
70271
70797
  expose_as_tool: definition.exposeAsTool || false,
70272
70798
  primary: definition.primary !== false,
70273
70799
  tool_description: definition.toolDescription || null,
70800
+ parent_hooks: definition.parentHooks || [],
70274
70801
  env: agentEnv,
70275
70802
  tenvs: agentEnv,
70276
70803
  // deprecated alias