@standardagents/builder 0.39.1 → 0.40.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.
Files changed (43) hide show
  1. package/dist/built-in-routes.js +609 -94
  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-0NI3m5DR.d.ts} +8 -2
  28. package/dist/index.d.ts +4 -4
  29. package/dist/index.js +1299 -758
  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.d.ts +12 -1
  35. package/dist/plugin.js +299 -19
  36. package/dist/plugin.js.map +1 -1
  37. package/dist/runtime.d.ts +12 -5
  38. package/dist/runtime.js +1009 -750
  39. package/dist/runtime.js.map +1 -1
  40. package/dist/test.d.ts +2 -2
  41. package/dist/{types-Bpe7IANZ.d.ts → types-BMnBYNwn.d.ts} +3 -1
  42. package/package.json +5 -5
  43. package/dist/client/index-eGECJFYe.js +0 -6
@@ -6680,6 +6680,255 @@ var init_context = __esm({
6680
6680
  }
6681
6681
  });
6682
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
+
6683
6932
  // src/agents/FlowEngine.ts
6684
6933
  var FlowEngine_exports = {};
6685
6934
  __export(FlowEngine_exports, {
@@ -6696,6 +6945,7 @@ var init_FlowEngine = __esm({
6696
6945
  init_context();
6697
6946
  init_sessionTools();
6698
6947
  init_ThreadStateImpl();
6948
+ init_ParentHookResolver();
6699
6949
  IMMEDIATE_BOOTSTRAP_TOOL_NAME = "bootstrap_immediate_subagent";
6700
6950
  HookBlockedExecutionError = class extends Error {
6701
6951
  constructor(message) {
@@ -7038,7 +7288,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
7038
7288
  _variables: def.variables ?? [],
7039
7289
  _env: def.env ?? def.tenvs ?? {},
7040
7290
  _requiredSchema: def.requiredSchema ?? null,
7041
- _hooks: def.hooks ?? []
7291
+ _hooks: def.hooks ?? [],
7292
+ _package: def.__package
7042
7293
  };
7043
7294
  }
7044
7295
  /**
@@ -7428,6 +7679,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
7428
7679
  type: newAgentDef.type,
7429
7680
  created_at: Date.now(),
7430
7681
  max_session_turns: newAgentDef.maxSessionTurns ?? null,
7682
+ hooks: newAgentDef.hooks ?? [],
7683
+ _package: newAgentDef.__package,
7431
7684
  side_a_label: newAgentDef.sideA?.label ?? null,
7432
7685
  side_a_agent_prompt: newAgentDef.sideA?.prompt ?? null,
7433
7686
  side_a_stop_on_response: newAgentDef.sideA?.stopOnResponse ?? true,
@@ -7588,7 +7841,7 @@ This usually points to something I can't fix from here \u2014 for example the wo
7588
7841
  return null;
7589
7842
  }
7590
7843
  return await stub.resolveThreadEnv({
7591
- threadId: state.threadId,
7844
+ threadId: state.thread.metadata.id,
7592
7845
  property,
7593
7846
  promptName: state.prompt?.name
7594
7847
  });
@@ -8110,8 +8363,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
8110
8363
  state.context[key] = existing;
8111
8364
  }
8112
8365
  static async runImmediateToolsIfNeeded(state) {
8113
- const promptTools = Array.isArray(state.prompt._tools) ? state.prompt._tools : [];
8114
- if (promptTools.length === 0) {
8366
+ const promptTools2 = Array.isArray(state.prompt._tools) ? state.prompt._tools : [];
8367
+ if (promptTools2.length === 0) {
8115
8368
  return false;
8116
8369
  }
8117
8370
  if (!state.immediatePromptRuns) {
@@ -8147,7 +8400,7 @@ This usually points to something I can't fix from here \u2014 for example the wo
8147
8400
  (activeInstanceCounts.get(child.name) ?? 0) + 1
8148
8401
  );
8149
8402
  }
8150
- for (const toolConfig of promptTools) {
8403
+ for (const toolConfig of promptTools2) {
8151
8404
  if (!toolConfig || typeof toolConfig !== "object") {
8152
8405
  continue;
8153
8406
  }
@@ -8313,10 +8566,10 @@ This usually points to something I can't fix from here \u2014 for example the wo
8313
8566
  * model's to clean up, and terminate_subagent on an infrastructure child
8314
8567
  * kills work the runtime depends on. Static and pure for direct testing.
8315
8568
  */
8316
- static filterModelVisibleSubagents(registry, promptTools) {
8569
+ static filterModelVisibleSubagents(registry, promptTools2) {
8317
8570
  if (registry.length === 0) return registry;
8318
8571
  const hidden = /* @__PURE__ */ new Set();
8319
- for (const config of promptTools ?? []) {
8572
+ for (const config of promptTools2 ?? []) {
8320
8573
  if (typeof config === "object" && config !== null && "name" in config && config.hidden === true) {
8321
8574
  hidden.add(config.name);
8322
8575
  }
@@ -9346,8 +9599,8 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9346
9599
  try {
9347
9600
  const nativeTools = await this.loadNativeTools(state);
9348
9601
  tools.push(...nativeTools);
9349
- const promptTools = await this.loadPromptTools(state);
9350
- tools.push(...promptTools);
9602
+ const promptTools2 = await this.loadPromptTools(state);
9603
+ tools.push(...promptTools2);
9351
9604
  const agentTools = await this.loadAgentTools(state);
9352
9605
  tools.push(...agentTools);
9353
9606
  const toolNameSources = /* @__PURE__ */ new Map();
@@ -9356,7 +9609,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9356
9609
  sources.add("native tool");
9357
9610
  toolNameSources.set(tool3.function.name, sources);
9358
9611
  }
9359
- for (const tool3 of promptTools) {
9612
+ for (const tool3 of promptTools2) {
9360
9613
  const sources = toolNameSources.get(tool3.function.name) || /* @__PURE__ */ new Set();
9361
9614
  sources.add("prompt");
9362
9615
  toolNameSources.set(tool3.function.name, sources);
@@ -9498,24 +9751,8 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9498
9751
  // Hook Resolution Helpers
9499
9752
  // ==========================================================================
9500
9753
  /**
9501
- * Get the list of hook IDs that should run for the current context.
9502
- * Resolution: prompt hooks > agent hooks > empty array
9503
- *
9504
- * @param state - Flow state with prompt and agent configuration
9505
- * @returns Array of hook IDs to execute
9506
- */
9507
- static getActiveHookIds(state) {
9508
- if (state.prompt?._hooks !== void 0 && state.prompt._hooks.length > 0) {
9509
- return state.prompt._hooks;
9510
- }
9511
- if (state.agentConfig.hooks !== void 0 && state.agentConfig.hooks.length > 0) {
9512
- return state.agentConfig.hooks;
9513
- }
9514
- return [];
9515
- }
9516
- /**
9517
- * Get hooks of a specific type from the active hook IDs.
9518
- * 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.
9519
9756
  *
9520
9757
  * @template K - The hook type name (ensures type-safe executors)
9521
9758
  * @param state - Flow state with hook configuration
@@ -9523,24 +9760,17 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9523
9760
  * @returns Array of typed hook executor functions
9524
9761
  */
9525
9762
  static async getHooksForType(state, hookType) {
9526
- const hookIds = this.getActiveHookIds(state);
9527
- if (hookIds.length === 0) return [];
9528
- const hooksRegistry = state.thread.instance.hooks();
9529
- if (!hooksRegistry) return [];
9530
- const executors = [];
9531
- for (const hookId of hookIds) {
9532
- const hookLoader = hooksRegistry[hookId];
9533
- if (!hookLoader) continue;
9534
- try {
9535
- const hookDef = await hookLoader();
9536
- if (hookDef && hookDef.hook === hookType) {
9537
- executors.push(hookDef.execute);
9538
- }
9539
- } catch (error) {
9540
- console.error(`[Hooks] Failed to load hook '${hookId}':`, error);
9541
- }
9542
- }
9543
- 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
+ );
9544
9774
  }
9545
9775
  // ==========================================================================
9546
9776
  // Hook Runner Methods
@@ -9558,9 +9788,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9558
9788
  if (executors.length === 0) return messages;
9559
9789
  let result = messages;
9560
9790
  const threadState = ThreadStateImpl.fromFlowState(state);
9561
- for (const executor of executors) {
9791
+ for (const { execute, context } of executors) {
9562
9792
  try {
9563
- result = await executor(threadState, result);
9793
+ result = await execute(threadState, result, context);
9564
9794
  } catch (error) {
9565
9795
  if (error && typeof error === "object" && error.block === true) {
9566
9796
  throw new HookBlockedExecutionError(error instanceof Error ? error.message : String(error));
@@ -9578,9 +9808,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9578
9808
  if (executors.length === 0) return systemMessage;
9579
9809
  let result = systemMessage;
9580
9810
  const threadState = ThreadStateImpl.fromFlowState(state);
9581
- for (const executor of executors) {
9811
+ for (const { execute, context } of executors) {
9582
9812
  try {
9583
- const next = await executor(threadState, result);
9813
+ const next = await execute(threadState, result, context);
9584
9814
  if (next !== null && next !== void 0) {
9585
9815
  result = next;
9586
9816
  }
@@ -9597,9 +9827,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9597
9827
  const executors = await this.getHooksForType(state, "after_subagent_created");
9598
9828
  if (executors.length === 0) return;
9599
9829
  const threadState = ThreadStateImpl.fromFlowState(state);
9600
- for (const executor of executors) {
9830
+ for (const { execute, context } of executors) {
9601
9831
  try {
9602
- await executor(threadState, childState);
9832
+ await execute(threadState, childState, context);
9603
9833
  } catch (error) {
9604
9834
  console.error("[Hooks] \u2717 Error running after_subagent_created hook:", error);
9605
9835
  }
@@ -9618,9 +9848,13 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9618
9848
  if (executors.length === 0) return message;
9619
9849
  let result = message;
9620
9850
  const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
9621
- for (const executor of executors) {
9851
+ for (const { execute, context } of executors) {
9622
9852
  try {
9623
- const hooked = await executor(threadState, _FlowEngine.rowToHookMessage(result));
9853
+ const hooked = await execute(
9854
+ threadState,
9855
+ _FlowEngine.rowToHookMessage(result),
9856
+ context
9857
+ );
9624
9858
  result = {
9625
9859
  ...result,
9626
9860
  ...hooked,
@@ -9646,9 +9880,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9646
9880
  const executors = await this.getHooksForType(state, "after_create_message");
9647
9881
  if (executors.length === 0) return;
9648
9882
  const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
9649
- for (const executor of executors) {
9883
+ for (const { execute, context } of executors) {
9650
9884
  try {
9651
- await executor(threadState, _FlowEngine.rowToHookMessage(message));
9885
+ await execute(threadState, _FlowEngine.rowToHookMessage(message), context);
9652
9886
  } catch (error) {
9653
9887
  console.error("[Hooks] \u2717 Error running after_create_message hook:", error);
9654
9888
  }
@@ -9689,9 +9923,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9689
9923
  if (executors.length === 0) return updates;
9690
9924
  let result = updates;
9691
9925
  const threadState = ThreadStateImpl.fromFlowState(state);
9692
- for (const executor of executors) {
9926
+ for (const { execute, context } of executors) {
9693
9927
  try {
9694
- result = await executor(threadState, messageId, result);
9928
+ result = await execute(threadState, messageId, result, context);
9695
9929
  } catch (error) {
9696
9930
  console.error("[Hooks] \u2717 Error running before_update_message hook:", error);
9697
9931
  }
@@ -9709,9 +9943,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9709
9943
  const executors = await this.getHooksForType(state, "after_update_message");
9710
9944
  if (executors.length === 0) return;
9711
9945
  const threadState = ThreadStateImpl.fromFlowState(state);
9712
- for (const executor of executors) {
9946
+ for (const { execute, context } of executors) {
9713
9947
  try {
9714
- await executor(threadState, _FlowEngine.rowToHookMessage(message));
9948
+ await execute(threadState, _FlowEngine.rowToHookMessage(message), context);
9715
9949
  } catch (error) {
9716
9950
  console.error("[Hooks] \u2717 Error running after_update_message hook:", error);
9717
9951
  }
@@ -9731,9 +9965,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9731
9965
  if (executors.length === 0) return toolResult;
9732
9966
  let result = toolResult;
9733
9967
  const threadState = ThreadStateImpl.fromFlowState(state);
9734
- for (const executor of executors) {
9968
+ for (const { execute, context } of executors) {
9735
9969
  try {
9736
- result = await executor(threadState, toolCall, result);
9970
+ result = await execute(threadState, toolCall, result, context);
9737
9971
  } catch (error) {
9738
9972
  console.error("[Hooks] \u2717 Error running before_store_tool_result hook:", error);
9739
9973
  }
@@ -9753,10 +9987,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9753
9987
  if (executors.length === 0) return toolResult;
9754
9988
  let result = toolResult;
9755
9989
  const threadState = ThreadStateImpl.fromFlowState(state);
9756
- for (const executor of executors) {
9990
+ for (const { execute, context } of executors) {
9757
9991
  if (result === null) break;
9758
9992
  try {
9759
- result = await executor(threadState, toolCall, result);
9993
+ result = await execute(threadState, toolCall, result, context);
9760
9994
  } catch (error) {
9761
9995
  console.error("[Hooks] \u2717 Error running after_tool_call_success hook:", error);
9762
9996
  }
@@ -9776,10 +10010,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9776
10010
  if (executors.length === 0) return toolResult;
9777
10011
  let result = toolResult;
9778
10012
  const threadState = ThreadStateImpl.fromFlowState(state);
9779
- for (const executor of executors) {
10013
+ for (const { execute, context } of executors) {
9780
10014
  if (result === null) break;
9781
10015
  try {
9782
- result = await executor(threadState, toolCall, result);
10016
+ result = await execute(threadState, toolCall, result, context);
9783
10017
  } catch (error) {
9784
10018
  console.error("[Hooks] \u2717 Error running after_tool_call_failure hook:", error);
9785
10019
  }
@@ -9809,9 +10043,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9809
10043
  const executors = await this.getHooksForType(state, "tool_call_started");
9810
10044
  if (executors.length === 0) return;
9811
10045
  const threadState = ThreadStateImpl.fromFlowState(state);
9812
- for (const executor of executors) {
10046
+ for (const { execute, context } of executors) {
9813
10047
  try {
9814
- await executor(threadState, toolCall, progress);
10048
+ await execute(threadState, toolCall, progress, context);
9815
10049
  } catch (error) {
9816
10050
  console.error("[Hooks] \u2717 Error running tool_call_started hook:", error);
9817
10051
  }
@@ -9839,9 +10073,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9839
10073
  const executors = await this.getHooksForType(state, "tool_call_done");
9840
10074
  if (executors.length === 0) return;
9841
10075
  const threadState = ThreadStateImpl.fromFlowState(state);
9842
- for (const executor of executors) {
10076
+ for (const { execute, context } of executors) {
9843
10077
  try {
9844
- await executor(threadState, toolCall, toolResult);
10078
+ await execute(threadState, toolCall, toolResult, context);
9845
10079
  } catch (error) {
9846
10080
  console.error("[Hooks] \u2717 Error running tool_call_done hook:", error);
9847
10081
  }
@@ -10794,12 +11028,12 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10794
11028
  if (executors.length === 0) return messages;
10795
11029
  let result = messages;
10796
11030
  const threadState = ThreadStateImpl.fromFlowState(state);
10797
- for (const executor of executors) {
11031
+ for (const { execute, context } of executors) {
10798
11032
  try {
10799
11033
  const byId = /* @__PURE__ */ new Map();
10800
11034
  for (const row of result) if (row.id) byId.set(row.id, row);
10801
11035
  const hookView = result.map((row) => _FlowEngine.rowToHookMessage(row));
10802
- const kept = await executor(threadState, hookView);
11036
+ const kept = await execute(threadState, hookView, context);
10803
11037
  result = kept.map((hookedMessage) => {
10804
11038
  const original = hookedMessage.id ? byId.get(hookedMessage.id) : void 0;
10805
11039
  if (original) {
@@ -11017,7 +11251,12 @@ function queueTool(flow, toolName, args = {}) {
11017
11251
  }
11018
11252
  async function injectMessage(flow, options) {
11019
11253
  const messageId = options.id ?? crypto.randomUUID();
11020
- const timestamp = Date.now() * TIMESTAMP_MULTIPLIER;
11254
+ const wallClockTimestamp = Date.now() * TIMESTAMP_MULTIPLIER;
11255
+ const latestLiveTimestamp = flow.messageHistory.reduce(
11256
+ (latest, entry) => Math.max(latest, entry.created_at),
11257
+ 0
11258
+ );
11259
+ const timestamp = options.beforeMessageId ? wallClockTimestamp : Math.max(wallClockTimestamp, latestLiveTimestamp + 1);
11021
11260
  const silent = options.silent ?? false;
11022
11261
  const forceTopLevel = options.forceTopLevel ?? false;
11023
11262
  let message = {
@@ -14803,11 +15042,19 @@ async function extractPromptTools(sourceCode) {
14803
15042
  result.tools.push(element.text);
14804
15043
  } else if (ts.isObjectLiteralExpression(element)) {
14805
15044
  result.hasSubpromptConfigs = true;
15045
+ let configuredName;
15046
+ let implementingAgent;
14806
15047
  for (const objProp of element.properties) {
14807
- if (ts.isPropertyAssignment(objProp) && ts.isIdentifier(objProp.name) && objProp.name.text === "name" && ts.isStringLiteral(objProp.initializer)) {
14808
- result.tools.push(objProp.initializer.text);
15048
+ if (ts.isPropertyAssignment(objProp) && ts.isIdentifier(objProp.name) && ts.isStringLiteral(objProp.initializer)) {
15049
+ if (objProp.name.text === "name") {
15050
+ configuredName = objProp.initializer.text;
15051
+ } else if (objProp.name.text === "agent") {
15052
+ implementingAgent = objProp.initializer.text;
15053
+ }
14809
15054
  }
14810
15055
  }
15056
+ const dependencyName = implementingAgent ?? configuredName;
15057
+ if (dependencyName) result.tools.push(dependencyName);
14811
15058
  }
14812
15059
  }
14813
15060
  }
@@ -14930,6 +15177,106 @@ async function extractAgentPrompts(sourceCode) {
14930
15177
  visit2(sourceFile);
14931
15178
  return result;
14932
15179
  }
15180
+ async function extractAgentHooks(sourceCode) {
15181
+ const ts = await loadTypeScript();
15182
+ const sourceFile = ts.createSourceFile(
15183
+ "agent.ts",
15184
+ sourceCode,
15185
+ ts.ScriptTarget.ESNext,
15186
+ true
15187
+ );
15188
+ const arrays = /* @__PURE__ */ new Map();
15189
+ const result = {
15190
+ hooks: [],
15191
+ parentHooks: [],
15192
+ parentHookRequirements: []
15193
+ };
15194
+ const unwrapArray = (expression) => {
15195
+ let current = expression;
15196
+ while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isSatisfiesExpression(current)) {
15197
+ current = current.expression;
15198
+ }
15199
+ if (ts.isArrayLiteralExpression(current)) return current;
15200
+ if (ts.isIdentifier(current)) return arrays.get(current.text) ?? null;
15201
+ return null;
15202
+ };
15203
+ for (const statement of sourceFile.statements) {
15204
+ if (!ts.isVariableStatement(statement)) continue;
15205
+ for (const declaration of statement.declarationList.declarations) {
15206
+ if (ts.isIdentifier(declaration.name) && declaration.initializer) {
15207
+ const array44 = unwrapArray(declaration.initializer);
15208
+ if (array44) arrays.set(declaration.name.text, array44);
15209
+ }
15210
+ }
15211
+ }
15212
+ const pushUnique = (target, value) => {
15213
+ if (!target.includes(value)) target.push(value);
15214
+ };
15215
+ function visit2(node) {
15216
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "defineAgent" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
15217
+ for (const property of node.arguments[0].properties) {
15218
+ if (!ts.isPropertyAssignment(property) || !ts.isIdentifier(property.name)) continue;
15219
+ const array44 = unwrapArray(property.initializer);
15220
+ if (!array44) continue;
15221
+ if (property.name.text === "hooks") {
15222
+ for (const element of array44.elements) {
15223
+ if (ts.isStringLiteral(element)) pushUnique(result.hooks, element.text);
15224
+ }
15225
+ }
15226
+ if (property.name.text === "parentHooks") {
15227
+ for (const element of array44.elements) {
15228
+ if (!ts.isObjectLiteralExpression(element)) continue;
15229
+ let hook;
15230
+ let reason;
15231
+ let stage;
15232
+ for (const requirement of element.properties) {
15233
+ if (ts.isPropertyAssignment(requirement) && ts.isIdentifier(requirement.name) && ts.isStringLiteral(requirement.initializer)) {
15234
+ if (requirement.name.text === "hook") hook = requirement.initializer.text;
15235
+ if (requirement.name.text === "reason") reason = requirement.initializer.text;
15236
+ if (requirement.name.text === "stage" && (requirement.initializer.text === "before_parent" || requirement.initializer.text === "after_parent")) {
15237
+ stage = requirement.initializer.text;
15238
+ }
15239
+ }
15240
+ }
15241
+ if (hook) {
15242
+ pushUnique(result.parentHooks, hook);
15243
+ if (reason) result.parentHookRequirements.push({ hook, reason, stage });
15244
+ }
15245
+ }
15246
+ }
15247
+ }
15248
+ }
15249
+ ts.forEachChild(node, visit2);
15250
+ }
15251
+ visit2(sourceFile);
15252
+ return result;
15253
+ }
15254
+ async function extractPromptHooks(sourceCode) {
15255
+ const ts = await loadTypeScript();
15256
+ const sourceFile = ts.createSourceFile(
15257
+ "prompt.ts",
15258
+ sourceCode,
15259
+ ts.ScriptTarget.ESNext,
15260
+ true
15261
+ );
15262
+ const hooks = [];
15263
+ function visit2(node) {
15264
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "definePrompt" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
15265
+ for (const property of node.arguments[0].properties) {
15266
+ if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.text === "hooks" && ts.isArrayLiteralExpression(property.initializer)) {
15267
+ for (const element of property.initializer.elements) {
15268
+ if (ts.isStringLiteral(element) && !hooks.includes(element.text)) {
15269
+ hooks.push(element.text);
15270
+ }
15271
+ }
15272
+ }
15273
+ }
15274
+ }
15275
+ ts.forEachChild(node, visit2);
15276
+ }
15277
+ visit2(sourceFile);
15278
+ return hooks;
15279
+ }
14933
15280
  async function extractAgentDescription(sourceCode) {
14934
15281
  const ts = await loadTypeScript();
14935
15282
  const sourceFile = ts.createSourceFile(
@@ -15185,6 +15532,13 @@ var init_packing_service = __esm({
15185
15532
  });
15186
15533
  const agentSource = fs4.readFileSync(agentFilePath, "utf-8");
15187
15534
  const agentPrompts = await extractAgentPrompts(agentSource);
15535
+ const agentHooks = await extractAgentHooks(agentSource);
15536
+ for (const hookId of agentHooks.hooks) {
15537
+ await this.analyzeHook(hookId, agentsDir, analysis, false, `agent:${agentName}`);
15538
+ }
15539
+ for (const hookId of agentHooks.parentHooks) {
15540
+ await this.analyzeHook(hookId, agentsDir, analysis, true, `agent:${agentName}`);
15541
+ }
15188
15542
  if (agentPrompts.sideA) {
15189
15543
  analysis.primaryPrompt = agentPrompts.sideA;
15190
15544
  await this.analyzePrompt(agentPrompts.sideA, agentsDir, analysis, /* @__PURE__ */ new Set(), `agent:${agentName}`);
@@ -15387,6 +15741,9 @@ var init_packing_service = __esm({
15387
15741
  }
15388
15742
  visited.add(`prompt:${promptName}`);
15389
15743
  const promptSource = fs4.readFileSync(promptFilePath, "utf-8");
15744
+ for (const hookId of await extractPromptHooks(promptSource)) {
15745
+ await this.analyzeHook(hookId, agentsDir, analysis, false, thisKey);
15746
+ }
15390
15747
  const modelName = await extractPromptModel(promptSource);
15391
15748
  if (modelName) {
15392
15749
  await this.analyzeModel(modelName, agentsDir, analysis, visited, thisKey);
@@ -15471,6 +15828,13 @@ var init_packing_service = __esm({
15471
15828
  visited.add(`agent:${agentName}`);
15472
15829
  const agentSource = fs4.readFileSync(agentFilePath, "utf-8");
15473
15830
  const agentPrompts = await extractAgentPrompts(agentSource);
15831
+ const agentHooks = await extractAgentHooks(agentSource);
15832
+ for (const hookId of agentHooks.hooks) {
15833
+ await this.analyzeHook(hookId, agentsDir, analysis, false, thisKey);
15834
+ }
15835
+ for (const hookId of agentHooks.parentHooks) {
15836
+ await this.analyzeHook(hookId, agentsDir, analysis, true, thisKey);
15837
+ }
15474
15838
  if (agentPrompts.sideA) {
15475
15839
  await this.analyzePrompt(agentPrompts.sideA, agentsDir, analysis, visited, thisKey);
15476
15840
  }
@@ -15478,6 +15842,27 @@ var init_packing_service = __esm({
15478
15842
  await this.analyzePrompt(agentPrompts.sideB, agentsDir, analysis, visited, thisKey);
15479
15843
  }
15480
15844
  }
15845
+ /** Add a hook referenced by an agent or prompt to the packed graph. */
15846
+ async analyzeHook(hookId, agentsDir, analysis, required, parentKey) {
15847
+ const hookFilePath = await this.findFile(path4.join(agentsDir, "hooks"), hookId);
15848
+ if (!hookFilePath) {
15849
+ const message = `Hook file not found for ID: ${hookId}`;
15850
+ if (required) analysis.errors.push(message);
15851
+ else analysis.warnings.push(message);
15852
+ return;
15853
+ }
15854
+ if (!analysis.constituents.hooks.some(
15855
+ (hook) => hook.name === hookId && hook.parentKey === parentKey
15856
+ )) {
15857
+ analysis.constituents.hooks.push({
15858
+ name: hookId,
15859
+ filePath: hookFilePath,
15860
+ discoveredVia: "static",
15861
+ sharedWith: [],
15862
+ parentKey
15863
+ });
15864
+ }
15865
+ }
15481
15866
  /**
15482
15867
  * Analyze a model and its fallbacks.
15483
15868
  */
@@ -15612,11 +15997,17 @@ var init_packing_service = __esm({
15612
15997
  } = options;
15613
15998
  const finalPackageName = packageName || packageId;
15614
15999
  const packedAt = Date.now();
16000
+ const entryAgentFile = await this.findFile(
16001
+ path4.join(rootDir, "agents", "agents"),
16002
+ agentName
16003
+ );
16004
+ const entryParentHooks = entryAgentFile ? (await extractAgentHooks(fs4.readFileSync(entryAgentFile, "utf-8"))).parentHookRequirements : [];
15615
16005
  const meta = {
15616
16006
  packageId,
15617
16007
  version,
15618
16008
  entryAgents: [agentName],
15619
- packedAt
16009
+ packedAt,
16010
+ ...entryParentHooks.length > 0 ? { parentHooks: { [agentName]: entryParentHooks } } : {}
15620
16011
  };
15621
16012
  const result = {
15622
16013
  success: false,
@@ -15725,6 +16116,7 @@ var init_packing_service = __esm({
15725
16116
  agentName,
15726
16117
  this.deduplicateConstituents(analysis).effects.map((effect) => effect.name),
15727
16118
  this.deduplicateConstituents(analysis).threadEndpoints.map((endpoint) => endpoint.name),
16119
+ meta.parentHooks,
15728
16120
  externalDeps,
15729
16121
  rootDir,
15730
16122
  license,
@@ -16116,7 +16508,7 @@ var init_packing_service = __esm({
16116
16508
  /**
16117
16509
  * Generate the package.json file with dependencies.
16118
16510
  */
16119
- generatePackageJson(packageName, version, agentName, effects, threadEndpoints, externalDeps, rootDir, license, licenseOwner) {
16511
+ generatePackageJson(packageName, version, agentName, effects, threadEndpoints, parentHooks, externalDeps, rootDir, license, licenseOwner) {
16120
16512
  const dependencies = {
16121
16513
  "@standardagents/spec": this.resolvePackageVersion("@standardagents/spec", rootDir)
16122
16514
  };
@@ -16138,6 +16530,7 @@ var init_packing_service = __esm({
16138
16530
  keywords: ["standardagent"],
16139
16531
  standardagent: {
16140
16532
  entryAgents: [agentName],
16533
+ ...parentHooks ? { parentHooks } : {},
16141
16534
  ...effects.length > 0 ? { effects } : {},
16142
16535
  ...threadEndpoints.length > 0 ? { threadEndpoints } : {}
16143
16536
  },
@@ -16433,6 +16826,22 @@ ${license || "See LICENSE file"}
16433
16826
  };
16434
16827
  }
16435
16828
  });
16829
+ function validParentHooks(value) {
16830
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
16831
+ return Object.values(value).every((requirements) => {
16832
+ if (!Array.isArray(requirements)) return false;
16833
+ const seen = /* @__PURE__ */ new Set();
16834
+ return requirements.every((requirement) => {
16835
+ if (typeof requirement !== "object" || requirement === null) return false;
16836
+ const item = requirement;
16837
+ 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)) {
16838
+ return false;
16839
+ }
16840
+ seen.add(item.hook);
16841
+ return true;
16842
+ });
16843
+ });
16844
+ }
16436
16845
  var PackageDiscoveryService;
16437
16846
  var init_discovery = __esm({
16438
16847
  "src/packing/discovery.ts"() {
@@ -16531,6 +16940,7 @@ var init_discovery = __esm({
16531
16940
  }
16532
16941
  const effects = Array.isArray(pkgJson.standardagent.effects) && pkgJson.standardagent.effects.every((effectName) => typeof effectName === "string") ? pkgJson.standardagent.effects : void 0;
16533
16942
  const threadEndpoints = Array.isArray(pkgJson.standardagent.threadEndpoints) && pkgJson.standardagent.threadEndpoints.every((endpoint) => typeof endpoint === "string") ? pkgJson.standardagent.threadEndpoints : void 0;
16943
+ const parentHooks = validParentHooks(pkgJson.standardagent.parentHooks) ? pkgJson.standardagent.parentHooks : void 0;
16534
16944
  return {
16535
16945
  packageId: pkgJson.name || pkgName,
16536
16946
  version: pkgJson.version || "0.0.0",
@@ -16539,6 +16949,7 @@ var init_discovery = __esm({
16539
16949
  entryAgents: pkgJson.standardagent.entryAgents,
16540
16950
  ...effects ? { effects } : {},
16541
16951
  ...threadEndpoints ? { threadEndpoints } : {},
16952
+ ...parentHooks ? { parentHooks } : {},
16542
16953
  description: pkgJson.description
16543
16954
  };
16544
16955
  } catch (error) {
@@ -16600,6 +17011,7 @@ var init_discovery = __esm({
16600
17011
  }
16601
17012
  const effects = Array.isArray(pkgJson.standardagent.effects) && pkgJson.standardagent.effects.every((effectName) => typeof effectName === "string") ? pkgJson.standardagent.effects : void 0;
16602
17013
  const threadEndpoints = Array.isArray(pkgJson.standardagent.threadEndpoints) && pkgJson.standardagent.threadEndpoints.every((endpoint) => typeof endpoint === "string") ? pkgJson.standardagent.threadEndpoints : void 0;
17014
+ const parentHooks = validParentHooks(pkgJson.standardagent.parentHooks) ? pkgJson.standardagent.parentHooks : void 0;
16603
17015
  return {
16604
17016
  packageId: pkgJson.name || dirName,
16605
17017
  version: pkgJson.version || "0.0.0",
@@ -16608,6 +17020,7 @@ var init_discovery = __esm({
16608
17020
  entryAgents: pkgJson.standardagent.entryAgents,
16609
17021
  ...effects ? { effects } : {},
16610
17022
  ...threadEndpoints ? { threadEndpoints } : {},
17023
+ ...parentHooks ? { parentHooks } : {},
16611
17024
  description: pkgJson.description
16612
17025
  };
16613
17026
  } catch (error) {
@@ -16636,7 +17049,8 @@ var init_discovery = __esm({
16636
17049
  const hasValidEntryAgents = Array.isArray(f.entryAgents) && f.entryAgents.length > 0 && f.entryAgents.every((e) => typeof e === "string");
16637
17050
  const hasValidEffects = f.effects === void 0 || Array.isArray(f.effects) && f.effects.every((effectName) => typeof effectName === "string");
16638
17051
  const hasValidThreadEndpoints = f.threadEndpoints === void 0 || Array.isArray(f.threadEndpoints) && f.threadEndpoints.every((endpoint) => typeof endpoint === "string");
16639
- return hasValidEntryAgents && hasValidEffects && hasValidThreadEndpoints;
17052
+ const hasValidParentHooks = f.parentHooks === void 0 || validParentHooks(f.parentHooks);
17053
+ return hasValidEntryAgents && hasValidEffects && hasValidThreadEndpoints && hasValidParentHooks;
16640
17054
  }
16641
17055
  /**
16642
17056
  * Get a package by ID.
@@ -67935,8 +68349,10 @@ var agents_get_default = defineController2(async ({ agents, agentNames, registry
67935
68349
  title: definition.title || definition.name,
67936
68350
  type: definition.type || "ai_human",
67937
68351
  description: definition.description || null,
68352
+ tool_description: definition.toolDescription || null,
67938
68353
  icon: definition.icon || null,
67939
68354
  primary: definition.primary !== false,
68355
+ parentHooks: definition.parentHooks || [],
67940
68356
  ...includeAuthoringDetails ? {
67941
68357
  default_prompt: definition.sideA?.prompt || "",
67942
68358
  default_model: "",
@@ -67972,8 +68388,10 @@ var agents_get_default = defineController2(async ({ agents, agentNames, registry
67972
68388
  title: definition.title || definition.name,
67973
68389
  type: definition.type || "ai_human",
67974
68390
  description: definition.description || null,
68391
+ tool_description: definition.toolDescription || null,
67975
68392
  icon: definition.icon || null,
67976
68393
  primary: definition.primary !== false,
68394
+ parentHooks: definition.parentHooks || [],
67977
68395
  ...includeAuthoringDetails ? {
67978
68396
  default_prompt: definition.sideA?.prompt || "",
67979
68397
  default_model: "",
@@ -68481,7 +68899,7 @@ var config_get_default = defineController2(async ({ config, env: env2, req }) =>
68481
68899
  });
68482
68900
 
68483
68901
  // src/version.ts
68484
- var BUILDER_VERSION = "0.39.1".length > 0 ? "0.39.1" : "dev";
68902
+ var BUILDER_VERSION = "0.40.1".length > 0 ? "0.40.1" : "dev";
68485
68903
 
68486
68904
  // src/api/diagnostics.get.ts
68487
68905
  init_LLMRequest();
@@ -69854,8 +70272,10 @@ migrations.reduce(
69854
70272
  );
69855
70273
 
69856
70274
  // src/durable-objects/DurableAgentBuilder.ts
70275
+ init_resolver();
69857
70276
  init_types();
69858
70277
  init_scope();
70278
+ init_ParentHookResolver();
69859
70279
  var THREAD_PREVIEW_MAX_CHARS = 140;
69860
70280
  var buildThreadPreview = (content) => {
69861
70281
  const collapsed = content.replace(/\s+/g, " ").trim();
@@ -70382,6 +70802,7 @@ var name_get_default = defineController2(async ({ params, agents, prompts, promp
70382
70802
  expose_as_tool: definition.exposeAsTool || false,
70383
70803
  primary: definition.primary !== false,
70384
70804
  tool_description: definition.toolDescription || null,
70805
+ parent_hooks: definition.parentHooks || [],
70385
70806
  env: agentEnv,
70386
70807
  tenvs: agentEnv,
70387
70808
  // deprecated alias
@@ -75862,6 +76283,10 @@ var stream_default3 = defineController2(async (context) => {
75862
76283
 
75863
76284
  // src/api/auth/device/[code]/_approval.ts
75864
76285
  var CONFIRMATION_TTL_SECONDS = 5 * 60;
76286
+ var BUILT_IN_DEVICE_APPROVAL_BRAND = {
76287
+ name: "Standard Agents",
76288
+ description: "Connect a new device to your account."
76289
+ };
75865
76290
  function escapeHtml(value) {
75866
76291
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
75867
76292
  }
@@ -75878,18 +76303,85 @@ function page(title, body, ok) {
75878
76303
  }
75879
76304
  );
75880
76305
  }
75881
- function confirmationPage(req, code, confirmationToken, handoff) {
75882
- const url = new URL(req.url);
76306
+ function optionalPresentationText(value, maxLength) {
76307
+ return typeof value === "string" && value.trim() && value.trim().length <= maxLength ? value.trim() : void 0;
76308
+ }
76309
+ function safePresentationLogo(value, allowRelative) {
76310
+ const logoUrl = optionalPresentationText(value, 2048);
76311
+ if (!logoUrl) return void 0;
76312
+ if (allowRelative && logoUrl.startsWith("/") && !logoUrl.startsWith("//") && !logoUrl.includes("#") && !logoUrl.includes("\\")) {
76313
+ return logoUrl;
76314
+ }
76315
+ try {
76316
+ const parsed = new URL(logoUrl);
76317
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.hash) return void 0;
76318
+ return parsed.toString();
76319
+ } catch {
76320
+ return void 0;
76321
+ }
76322
+ }
76323
+ function normalizeRuntimeBrand(value, platformShape) {
76324
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
76325
+ const record24 = value;
76326
+ const name15 = optionalPresentationText(record24.name, 100);
76327
+ if (!name15) return null;
76328
+ const description = optionalPresentationText(record24.description, 280);
76329
+ const logoValue = platformShape ? record24.logo_url : record24.logoUrl;
76330
+ const logoUrl = safePresentationLogo(logoValue, !platformShape);
76331
+ return {
76332
+ name: name15,
76333
+ ...description ? { description } : {},
76334
+ ...logoUrl ? { logoUrl } : {}
76335
+ };
76336
+ }
76337
+ async function resolveDeviceApprovalBrand(env2, config) {
76338
+ const configured = normalizeRuntimeBrand(config?.deviceApproval, false);
76339
+ const projectId = optionalPresentationText(
76340
+ env2.STANDARD_AGENTS_PROJECT_ID,
76341
+ 128
76342
+ );
76343
+ if (isPlatformHosted(env2) && projectId) {
76344
+ try {
76345
+ const url = new URL("/partners/auth/brand", `${platformEndpoint(env2)}/`);
76346
+ url.searchParams.set("project_id", projectId);
76347
+ const response = await fetch(url, {
76348
+ headers: { Accept: "application/json" },
76349
+ signal: AbortSignal.timeout(1e3)
76350
+ });
76351
+ if (response.ok) {
76352
+ const payload = await response.json();
76353
+ const authoritative = normalizeRuntimeBrand(payload.instance, true);
76354
+ if (authoritative) return authoritative;
76355
+ }
76356
+ } catch {
76357
+ }
76358
+ }
76359
+ return configured ?? BUILT_IN_DEVICE_APPROVAL_BRAND;
76360
+ }
76361
+ function approvalCsp(req, brand) {
76362
+ let imageSource = "";
76363
+ if (brand.logoUrl) {
76364
+ const requestOrigin = new URL(req.url).origin;
76365
+ const logoOrigin = new URL(brand.logoUrl, requestOrigin).origin;
76366
+ imageSource = `; img-src ${logoOrigin === requestOrigin ? "'self'" : logoOrigin}`;
76367
+ }
76368
+ return `default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'${imageSource}`;
76369
+ }
76370
+ function confirmationPage(req, confirmationToken, handoff, brand, identity) {
75883
76371
  const handoffInput = handoff ? `<input type="hidden" name="handoff" value="${escapeHtml(handoff)}">` : "";
75884
- const shortCode = code.length > 12 ? `${code.slice(0, 6)}\u2026${code.slice(-6)}` : code;
75885
- const html = `<!doctype html><html><head><meta charset="utf-8"><meta name="referrer" content="no-referrer"><title>Approve device sign-in</title><style>body{background:#0a0c10;color:#e8ecf3;font:16px/1.6 ui-sans-serif,system-ui;display:flex;min-height:100vh;align-items:center;justify-content:center}main{max-width:440px;text-align:center;padding:40px;border:1px solid #232936;border-radius:14px;background:#11141b}h1{font-size:22px}p{color:#a5adbd}.code{font:600 18px ui-monospace,monospace;letter-spacing:.08em;color:#e8ecf3}form{display:flex;gap:12px;justify-content:center;margin-top:26px}button,a{border-radius:9px;padding:10px 18px;font:inherit;text-decoration:none}button{border:0;background:#2dd4bf;color:#07110f;font-weight:700;cursor:pointer}a{border:1px solid #394150;color:#cbd5e1}</style></head><body><main><h1>Approve device sign-in?</h1><p>A command-line client is requesting a personal instance API key for your account.</p><p class="code">${escapeHtml(shortCode)}</p><form method="post" action="${escapeHtml(url.pathname)}"><input type="hidden" name="confirmation_token" value="${escapeHtml(confirmationToken)}">${handoffInput}<button type="submit">Approve device</button><a href="/">Cancel</a></form></main></body></html>`;
76372
+ const logo = brand.logoUrl ? `<img class="logo" src="${escapeHtml(brand.logoUrl)}" alt="">` : `<span class="logo-fallback" aria-hidden="true">${escapeHtml(brand.name.slice(0, 1).toUpperCase())}</span>`;
76373
+ const description = brand.description ? `<p class="description">${escapeHtml(brand.description)}</p>` : "";
76374
+ const secondaryIdentity = identity.displayEmail && identity.displayEmail !== identity.displayName ? `<span>${escapeHtml(identity.displayEmail)}</span>` : "";
76375
+ const html = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="referrer" content="no-referrer"><title>Connect device \xB7 ${escapeHtml(brand.name)}</title><style>
76376
+ :root{color-scheme:light dark;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#f6f7f9;color:#15171a}*{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;padding:28px;background:radial-gradient(circle at 50% 0,rgba(71,194,174,.12),transparent 38%),linear-gradient(#fbfcfd,#f2f4f6)}main{width:min(100%,480px)}.card{background:rgba(255,255,255,.92);border:1px solid #dfe3e8;border-radius:20px;padding:34px;box-shadow:0 24px 70px rgba(20,27,38,.12),0 2px 8px rgba(20,27,38,.06)}.brand{display:flex;align-items:center;gap:12px;margin-bottom:28px}.logo,.logo-fallback{width:38px;height:38px;border-radius:10px;display:grid;place-items:center;object-fit:contain}.logo-fallback{background:#171b21;color:#fff;font-weight:750}.brand strong{font-size:17px;letter-spacing:-.01em}h1{font-size:29px;line-height:1.18;letter-spacing:-.035em;margin:0 0 12px}p{margin:0;color:#5b6470;line-height:1.58}.description{margin-bottom:20px}.request{margin-top:18px}.identity{display:flex;align-items:center;gap:12px;margin-top:24px;padding:14px;border-radius:12px;background:#f2f4f6;border:1px solid #e3e6ea}.avatar{width:34px;height:34px;border-radius:50%;display:grid;place-items:center;background:#dfe4e8;color:#313841;font-weight:700}.identity-text{min-width:0;display:flex;flex-direction:column}.identity-label{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:#737d89}.identity strong,.identity span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.identity span{font-size:13px;color:#68727e}form{display:grid;gap:10px;margin-top:26px}button{appearance:none;border-radius:11px;padding:12px 16px;font:inherit;font-weight:700;cursor:pointer}.connect{border:1px solid #15191d;background:#15191d;color:#fff}.deny{border:1px solid transparent;background:transparent;color:#68727e}.deny:hover{background:#f1f3f5;color:#252a30}.disclosure{text-align:center;font-size:12px;color:#8a939d;margin:16px 0 0}@media(prefers-color-scheme:dark){:root{background:#090b0e;color:#f2f5f7}body{background:radial-gradient(circle at 50% 0,rgba(68,198,177,.12),transparent 38%),#090b0e}.card{background:rgba(17,20,25,.94);border-color:#292e36;box-shadow:0 28px 80px rgba(0,0,0,.42)}p{color:#a9b1bc}.identity{background:#191d23;border-color:#292f38}.avatar{background:#2b323b;color:#eef2f5}.identity-label,.identity span{color:#929ca8}.connect{background:#62d4c2;border-color:#62d4c2;color:#07110f}.deny{color:#9aa4af}.deny:hover{background:#1d2229;color:#e4e8ec}.disclosure{color:#747e8a}}
76377
+ </style></head><body><main><section class="card"><div class="brand">${logo}<strong>${escapeHtml(brand.name)}</strong></div><h1>Connect ${escapeHtml(brand.name)}?</h1>${description}<p class="request">A new device wants to connect to your account. Only continue if you started this sign-in.</p><div class="identity"><div class="avatar" aria-hidden="true">${escapeHtml(identity.displayName.slice(0, 1).toUpperCase())}</div><div class="identity-text"><span class="identity-label">Signed in as</span><strong>${escapeHtml(identity.displayName)}</strong>${secondaryIdentity}</div></div><form method="post" action=""><input type="hidden" name="confirmation_token" value="${escapeHtml(confirmationToken)}">${handoffInput}<button class="connect" type="submit" name="decision" value="approve">Connect device</button><button class="deny" type="submit" name="decision" value="deny">Not me</button></form></section><p class="disclosure">Authentication secured by Standard Agents</p></main></body></html>`;
75886
76378
  return new Response(html, {
75887
76379
  headers: {
75888
76380
  "Content-Type": "text/html; charset=utf-8",
75889
76381
  "Cache-Control": "no-store",
75890
76382
  "Referrer-Policy": "no-referrer",
75891
76383
  "X-Content-Type-Options": "nosniff",
75892
- "Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'"
76384
+ "Content-Security-Policy": approvalCsp(req, brand)
75893
76385
  }
75894
76386
  });
75895
76387
  }
@@ -75916,7 +76408,7 @@ async function resolveDeviceApprovalIdentity(req, env2) {
75916
76408
  if (!payload || !payload.platform_user_id || !payload.role || !payload.jti || !projectOk) {
75917
76409
  return outcome(env2, false, "signin_failed", {
75918
76410
  title: "Sign in failed",
75919
- body: "This sign-in link is invalid or has expired. Start again from your terminal."
76411
+ body: "This sign-in link is invalid or has expired. Start again from your device."
75920
76412
  });
75921
76413
  }
75922
76414
  const platformProfile = {
@@ -75931,6 +76423,8 @@ async function resolveDeviceApprovalIdentity(req, env2) {
75931
76423
  return {
75932
76424
  confirmationSubject: `platform:${payload.platform_user_id}`,
75933
76425
  handoff,
76426
+ displayName: payload.display_name?.trim() || payload.username?.trim() || payload.email?.trim() || "Your account",
76427
+ displayEmail: payload.email?.trim() || null,
75934
76428
  platformProfile,
75935
76429
  platformHandoff: {
75936
76430
  jti: payload.jti,
@@ -75962,6 +76456,8 @@ async function resolveDeviceApprovalIdentity(req, env2) {
75962
76456
  return {
75963
76457
  confirmationSubject: `user:${authResult.user.id}`,
75964
76458
  handoff: null,
76459
+ displayName: authResult.user.display_name?.trim() || authResult.user.username?.trim() || authResult.user.email?.trim() || "Your account",
76460
+ displayEmail: authResult.user.email?.trim() || null,
75965
76461
  localUserId: authResult.user.id
75966
76462
  };
75967
76463
  }
@@ -76020,7 +76516,7 @@ function isSameOriginApprovalPost(req) {
76020
76516
  }
76021
76517
 
76022
76518
  // src/api/auth/device/[code]/approve.get.ts
76023
- var approve_get_default = defineController2(async ({ req, env: env2, params }) => {
76519
+ var approve_get_default = defineController2(async ({ req, env: env2, params, config }) => {
76024
76520
  const code = params.code;
76025
76521
  if (!code) {
76026
76522
  return outcome(env2, false, "invalid_link", {
@@ -76041,7 +76537,8 @@ var approve_get_default = defineController2(async ({ req, env: env2, params }) =
76041
76537
  body: "This instance cannot create a secure device confirmation. Contact its administrator."
76042
76538
  });
76043
76539
  }
76044
- return confirmationPage(req, code, confirmationToken, identity.handoff);
76540
+ const brand = await resolveDeviceApprovalBrand(env2, config);
76541
+ return confirmationPage(req, confirmationToken, identity.handoff, brand, identity);
76045
76542
  });
76046
76543
 
76047
76544
  // src/api/auth/device/[code]/approve.post.ts
@@ -76058,6 +76555,10 @@ var approve_post_default = defineController2(async ({ req, env: env2, params })
76058
76555
  }
76059
76556
  const form = await req.clone().formData().catch(() => null);
76060
76557
  const confirmationToken = form?.get("confirmation_token")?.toString() ?? "";
76558
+ const decision = form?.get("decision")?.toString() || "approve";
76559
+ if (decision !== "approve" && decision !== "deny") {
76560
+ return Response.json({ error: "Invalid device confirmation decision" }, { status: 400 });
76561
+ }
76061
76562
  if (!confirmationToken) {
76062
76563
  return Response.json({ error: "Device confirmation token required" }, { status: 403 });
76063
76564
  }
@@ -76074,31 +76575,45 @@ var approve_post_default = defineController2(async ({ req, env: env2, params })
76074
76575
  if (!await consumeDeviceApprovalHandoff(env2, identity)) {
76075
76576
  return outcome(env2, false, "signin_failed", {
76076
76577
  title: "Sign in failed",
76077
- body: "This sign-in link was already used or has expired. Start again from your terminal."
76578
+ body: "This sign-in link was already used or has expired. Start again from your device."
76078
76579
  });
76079
76580
  }
76080
- const userId = await materializeDeviceApprovalUser(env2, identity);
76081
- const token = generateApiKey();
76082
76581
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
76083
76582
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
76583
+ const codeHash = await hashToken(code);
76584
+ if (decision === "deny") {
76585
+ const denied = await agentBuilder.denyDeviceLogin(codeHash);
76586
+ if (!denied) {
76587
+ return outcome(env2, false, "expired", {
76588
+ title: "Link expired",
76589
+ body: "This device sign-in has expired or was already decided. Start again from your device."
76590
+ });
76591
+ }
76592
+ return outcome(env2, false, "denied", {
76593
+ title: "Connection denied",
76594
+ body: "The device was not connected. You can close this window."
76595
+ });
76596
+ }
76597
+ const userId = await materializeDeviceApprovalUser(env2, identity);
76598
+ const token = generateApiKey();
76084
76599
  const ok = await agentBuilder.approveDeviceLogin({
76085
- code_hash: await hashToken(code),
76600
+ code_hash: codeHash,
76086
76601
  user_id: userId,
76087
76602
  token,
76088
76603
  token_hash: await hashToken(token),
76089
76604
  key_prefix: token.slice(0, 12),
76090
76605
  last_five: token.slice(-5),
76091
- key_name: "CLI device login"
76606
+ key_name: "Device login"
76092
76607
  });
76093
76608
  if (!ok) {
76094
76609
  return outcome(env2, false, "expired", {
76095
76610
  title: "Link expired",
76096
- body: "This device login has expired or was already used. Start again from your terminal."
76611
+ body: "This device sign-in has expired or was already decided. Start again from your device."
76097
76612
  });
76098
76613
  }
76099
76614
  return outcome(env2, true, "done", {
76100
76615
  title: "You're connected",
76101
- body: "Standard Code is now signed in. Return to your terminal \u2014 it will pick up automatically."
76616
+ body: "The device is now connected. Return to the app that started this sign-in."
76102
76617
  });
76103
76618
  });
76104
76619