@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
package/dist/index.js CHANGED
@@ -1498,6 +1498,9 @@ function normalizeToolCallMessageSequence(messages) {
1498
1498
  for (const message of messages) {
1499
1499
  const isAssistantToolCallMessage = message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
1500
1500
  if (!pending) {
1501
+ if (message.role === "tool") {
1502
+ continue;
1503
+ }
1501
1504
  if (!isAssistantToolCallMessage) {
1502
1505
  normalized.push(message);
1503
1506
  continue;
@@ -6806,6 +6809,547 @@ var init_context = __esm({
6806
6809
  }
6807
6810
  });
6808
6811
 
6812
+ // src/packing/resolver.ts
6813
+ function parseQualifiedName(name) {
6814
+ if (name.startsWith("@")) {
6815
+ const firstSlash = name.indexOf("/");
6816
+ if (firstSlash === -1) {
6817
+ return [null, name];
6818
+ }
6819
+ const secondSlash = name.indexOf("/", firstSlash + 1);
6820
+ if (secondSlash === -1) {
6821
+ return [null, name];
6822
+ }
6823
+ return [name.substring(0, secondSlash), name.substring(secondSlash + 1)];
6824
+ }
6825
+ const separatorIndex = name.indexOf("/");
6826
+ if (separatorIndex === -1) {
6827
+ return [null, name];
6828
+ }
6829
+ return [name.substring(0, separatorIndex), name.substring(separatorIndex + 1)];
6830
+ }
6831
+ function qualifyName(packageId, shortName) {
6832
+ return `${packageId}/${shortName}`;
6833
+ }
6834
+ function getShortName(name) {
6835
+ const [, shortName] = parseQualifiedName(name);
6836
+ return shortName;
6837
+ }
6838
+ function resolveTool(registry, name, namespace) {
6839
+ const [pkgId, shortName] = parseQualifiedName(name);
6840
+ if (pkgId !== null) {
6841
+ const pkg = registry.packages[pkgId];
6842
+ if (!pkg) {
6843
+ throw new NamespaceResolutionError(
6844
+ "tool",
6845
+ name,
6846
+ namespace,
6847
+ `Package "${pkgId}" not found`
6848
+ );
6849
+ }
6850
+ throw new NamespaceResolutionError(
6851
+ "tool",
6852
+ name,
6853
+ namespace,
6854
+ `Cannot access tool "${shortName}" from package "${pkgId}". Only entry point agents are accessible cross-package.`
6855
+ );
6856
+ }
6857
+ if (namespace.type === "packed") {
6858
+ const pkg = registry.packages[namespace.packageId];
6859
+ if (!pkg) {
6860
+ throw new NamespaceResolutionError(
6861
+ "tool",
6862
+ name,
6863
+ namespace,
6864
+ `Package "${namespace.packageId}" not found`
6865
+ );
6866
+ }
6867
+ const loader2 = pkg.tools[shortName];
6868
+ if (!loader2) {
6869
+ throw new NamespaceResolutionError(
6870
+ "tool",
6871
+ name,
6872
+ namespace,
6873
+ `Tool "${shortName}" not found in package "${namespace.packageId}"`
6874
+ );
6875
+ }
6876
+ return loader2;
6877
+ }
6878
+ const loader = registry.global.tools[shortName];
6879
+ if (!loader) {
6880
+ throw new NamespaceResolutionError(
6881
+ "tool",
6882
+ name,
6883
+ namespace,
6884
+ `Tool "${shortName}" not found in global namespace`
6885
+ );
6886
+ }
6887
+ return loader;
6888
+ }
6889
+ function resolvePrompt(registry, name, namespace) {
6890
+ const [pkgId, shortName] = parseQualifiedName(name);
6891
+ if (pkgId !== null) {
6892
+ throw new NamespaceResolutionError(
6893
+ "prompt",
6894
+ name,
6895
+ namespace,
6896
+ `Cannot access prompt "${shortName}" from package "${pkgId}". Prompts are package-internal.`
6897
+ );
6898
+ }
6899
+ if (namespace.type === "packed") {
6900
+ const pkg = registry.packages[namespace.packageId];
6901
+ if (!pkg) {
6902
+ throw new NamespaceResolutionError(
6903
+ "prompt",
6904
+ name,
6905
+ namespace,
6906
+ `Package "${namespace.packageId}" not found`
6907
+ );
6908
+ }
6909
+ const loader2 = pkg.prompts[shortName];
6910
+ if (!loader2) {
6911
+ throw new NamespaceResolutionError(
6912
+ "prompt",
6913
+ name,
6914
+ namespace,
6915
+ `Prompt "${shortName}" not found in package "${namespace.packageId}"`
6916
+ );
6917
+ }
6918
+ return loader2;
6919
+ }
6920
+ const loader = registry.global.prompts[shortName];
6921
+ if (!loader) {
6922
+ throw new NamespaceResolutionError(
6923
+ "prompt",
6924
+ name,
6925
+ namespace,
6926
+ `Prompt "${shortName}" not found in global namespace`
6927
+ );
6928
+ }
6929
+ return loader;
6930
+ }
6931
+ function resolveModel(registry, name, namespace) {
6932
+ const [possiblePkgId, shortName] = parseQualifiedName(name);
6933
+ if (possiblePkgId !== null && registry.packages[possiblePkgId]) {
6934
+ throw new NamespaceResolutionError(
6935
+ "model",
6936
+ name,
6937
+ namespace,
6938
+ `Cannot use qualified model name "${name}". Models must be unqualified. If this is a provider model name, ensure no package named "${possiblePkgId}" exists.`
6939
+ );
6940
+ }
6941
+ const lookupName = name;
6942
+ if (namespace.type === "packed") {
6943
+ const pkg = registry.packages[namespace.packageId];
6944
+ if (pkg) {
6945
+ const loader2 = pkg.models[lookupName];
6946
+ if (loader2) {
6947
+ return loader2;
6948
+ }
6949
+ }
6950
+ const globalLoader = registry.global.models[lookupName];
6951
+ if (globalLoader) {
6952
+ return globalLoader;
6953
+ }
6954
+ throw new NamespaceResolutionError(
6955
+ "model",
6956
+ name,
6957
+ namespace,
6958
+ `Model "${lookupName}" not found in package "${namespace.packageId}" or global namespace`
6959
+ );
6960
+ }
6961
+ const loader = registry.global.models[lookupName];
6962
+ if (!loader) {
6963
+ throw new NamespaceResolutionError(
6964
+ "model",
6965
+ name,
6966
+ namespace,
6967
+ `Model "${lookupName}" not found in global namespace`
6968
+ );
6969
+ }
6970
+ return loader;
6971
+ }
6972
+ function resolveAgent(registry, name, namespace) {
6973
+ const [pkgId, shortName] = parseQualifiedName(name);
6974
+ if (pkgId !== null) {
6975
+ const pkg = registry.packages[pkgId];
6976
+ if (!pkg) {
6977
+ throw new NamespaceResolutionError(
6978
+ "agent",
6979
+ name,
6980
+ namespace,
6981
+ `Package "${pkgId}" not found`
6982
+ );
6983
+ }
6984
+ if (!pkg.entryAgents.includes(shortName)) {
6985
+ throw new NamespaceResolutionError(
6986
+ "agent",
6987
+ name,
6988
+ namespace,
6989
+ `Agent "${shortName}" is not an entry point in package "${pkgId}"`
6990
+ );
6991
+ }
6992
+ const loader = pkg.agents[shortName];
6993
+ if (!loader) {
6994
+ throw new NamespaceResolutionError(
6995
+ "agent",
6996
+ name,
6997
+ namespace,
6998
+ `Agent "${shortName}" not found in package "${pkgId}"`
6999
+ );
7000
+ }
7001
+ return loader;
7002
+ }
7003
+ if (namespace.type === "packed") {
7004
+ const pkg = registry.packages[namespace.packageId];
7005
+ if (!pkg) {
7006
+ throw new NamespaceResolutionError(
7007
+ "agent",
7008
+ name,
7009
+ namespace,
7010
+ `Package "${namespace.packageId}" not found`
7011
+ );
7012
+ }
7013
+ const loader = pkg.agents[shortName];
7014
+ if (!loader) {
7015
+ throw new NamespaceResolutionError(
7016
+ "agent",
7017
+ name,
7018
+ namespace,
7019
+ `Agent "${shortName}" not found in package "${namespace.packageId}"`
7020
+ );
7021
+ }
7022
+ return loader;
7023
+ }
7024
+ const globalLoader = registry.global.agents[shortName];
7025
+ if (globalLoader) {
7026
+ return globalLoader;
7027
+ }
7028
+ for (const [pkgId2, pkg] of Object.entries(registry.packages)) {
7029
+ if (pkg.entryAgents.includes(shortName)) {
7030
+ const loader = pkg.agents[shortName];
7031
+ if (loader) {
7032
+ return loader;
7033
+ }
7034
+ }
7035
+ }
7036
+ throw new NamespaceResolutionError(
7037
+ "agent",
7038
+ name,
7039
+ namespace,
7040
+ `Agent "${shortName}" not found in global namespace`
7041
+ );
7042
+ }
7043
+ function resolveHook(registry, name, namespace) {
7044
+ const [pkgId, shortName] = parseQualifiedName(name);
7045
+ if (pkgId !== null) {
7046
+ throw new NamespaceResolutionError(
7047
+ "hook",
7048
+ name,
7049
+ namespace,
7050
+ `Cannot use qualified hook name "${name}". Hooks must be unqualified.`
7051
+ );
7052
+ }
7053
+ if (namespace.type === "packed") {
7054
+ const pkg = registry.packages[namespace.packageId];
7055
+ if (pkg) {
7056
+ const loader2 = pkg.hooks[shortName];
7057
+ if (loader2) {
7058
+ return loader2;
7059
+ }
7060
+ }
7061
+ return null;
7062
+ }
7063
+ const loader = registry.global.hooks[shortName];
7064
+ return loader || null;
7065
+ }
7066
+ function getVisibleToolNames(registry, namespace) {
7067
+ if (namespace.type === "packed") {
7068
+ const pkg = registry.packages[namespace.packageId];
7069
+ return pkg ? Object.keys(pkg.tools) : [];
7070
+ }
7071
+ return Object.keys(registry.global.tools);
7072
+ }
7073
+ function getVisibleAgentNames(registry, namespace) {
7074
+ if (namespace.type === "packed") {
7075
+ const pkg = registry.packages[namespace.packageId];
7076
+ const ownAgents = pkg ? Object.keys(pkg.agents) : [];
7077
+ const otherEntryPoints = Object.entries(registry.packages).filter(([id]) => id !== namespace.packageId).flatMap(
7078
+ ([pkgId, pkg2]) => pkg2.entryAgents.map((name) => qualifyName(pkgId, name))
7079
+ );
7080
+ return [...ownAgents, ...otherEntryPoints];
7081
+ }
7082
+ const globalAgents = Object.keys(registry.global.agents);
7083
+ const entryPoints = Object.values(registry.packages).flatMap((pkg) => pkg.entryAgents);
7084
+ return [.../* @__PURE__ */ new Set([...globalAgents, ...entryPoints])];
7085
+ }
7086
+ function getVisiblePromptNames(registry, namespace) {
7087
+ if (namespace.type === "packed") {
7088
+ const pkg = registry.packages[namespace.packageId];
7089
+ return pkg ? Object.keys(pkg.prompts) : [];
7090
+ }
7091
+ return Object.keys(registry.global.prompts);
7092
+ }
7093
+ function getVisibleModelNames(registry, namespace) {
7094
+ if (namespace.type === "packed") {
7095
+ const pkg = registry.packages[namespace.packageId];
7096
+ const ownModels = pkg ? Object.keys(pkg.models) : [];
7097
+ const globalModels = Object.keys(registry.global.models);
7098
+ return [.../* @__PURE__ */ new Set([...ownModels, ...globalModels])];
7099
+ }
7100
+ return Object.keys(registry.global.models);
7101
+ }
7102
+ function createNamespaceContext(agentDef) {
7103
+ if (agentDef?.__package) {
7104
+ return { type: "packed", packageId: agentDef.__package.packageId };
7105
+ }
7106
+ return { type: "global" };
7107
+ }
7108
+ function isQualifiedName(name) {
7109
+ if (name.startsWith("@")) {
7110
+ const firstSlash = name.indexOf("/");
7111
+ if (firstSlash === -1) return false;
7112
+ return name.indexOf("/", firstSlash + 1) !== -1;
7113
+ }
7114
+ return name.includes("/");
7115
+ }
7116
+ var NamespaceResolutionError;
7117
+ var init_resolver = __esm({
7118
+ "src/packing/resolver.ts"() {
7119
+ NamespaceResolutionError = class extends Error {
7120
+ constructor(itemType, itemName, namespace, message) {
7121
+ super(message);
7122
+ this.itemType = itemType;
7123
+ this.itemName = itemName;
7124
+ this.namespace = namespace;
7125
+ this.name = "NamespaceResolutionError";
7126
+ }
7127
+ };
7128
+ }
7129
+ });
7130
+
7131
+ // src/agents/ParentHookResolver.ts
7132
+ function packageOf(definition) {
7133
+ return definition?.__package ?? definition?._package;
7134
+ }
7135
+ function qualifyWithinPackage(name, packageId) {
7136
+ if (!packageId || parseQualifiedName(name)[0] !== null) {
7137
+ return name;
7138
+ }
7139
+ return qualifyName(packageId, name);
7140
+ }
7141
+ function promptHooks(prompt) {
7142
+ if (!prompt) return [];
7143
+ return Array.isArray(prompt.hooks) ? prompt.hooks : Array.isArray(prompt._hooks) ? prompt._hooks : [];
7144
+ }
7145
+ function promptTools(prompt) {
7146
+ if (!prompt) return [];
7147
+ return Array.isArray(prompt.tools) ? prompt.tools : Array.isArray(prompt._tools) ? prompt._tools : [];
7148
+ }
7149
+ function relationDescriptor(entry) {
7150
+ if (typeof entry === "string") {
7151
+ return {
7152
+ relationshipName: entry,
7153
+ implementingAgent: entry,
7154
+ explicitlyAgentShaped: false
7155
+ };
7156
+ }
7157
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
7158
+ return null;
7159
+ }
7160
+ const config = entry;
7161
+ if (typeof config.name !== "string" || !config.name) {
7162
+ return null;
7163
+ }
7164
+ const implementingAgent = typeof config.agent === "string" && config.agent ? config.agent : config.name;
7165
+ const explicitlyAgentShaped = "agent" in config || "blocking" in config || "resumable" in config || "immediate" in config || "parentCommunication" in config || "hidden" in config;
7166
+ return {
7167
+ relationshipName: config.name,
7168
+ implementingAgent,
7169
+ optional: typeof config.optional === "string" ? config.optional : void 0,
7170
+ explicitlyAgentShaped
7171
+ };
7172
+ }
7173
+ function hookLoader(host, owner, hookId) {
7174
+ const ownerPackage = packageOf(owner);
7175
+ if (!ownerPackage) {
7176
+ return host.hooks()[hookId];
7177
+ }
7178
+ return host.registry?.()?.packages?.[ownerPackage.packageId]?.hooks?.[hookId];
7179
+ }
7180
+ async function loadBinding(host, owner, hookId, hookType, context, required) {
7181
+ const loader = hookLoader(host, owner, hookId);
7182
+ if (!loader) {
7183
+ if (required) {
7184
+ throw new Error(
7185
+ `Required parent hook '${hookId}' was not found in the child agent namespace`
7186
+ );
7187
+ }
7188
+ return null;
7189
+ }
7190
+ let loaded;
7191
+ try {
7192
+ loaded = await loader();
7193
+ } catch (error) {
7194
+ if (required) throw error;
7195
+ console.error(`[Hooks] Failed to load hook '${hookId}':`, error);
7196
+ return null;
7197
+ }
7198
+ if (!loaded) {
7199
+ if (required) {
7200
+ throw new Error(`Required parent hook '${hookId}' did not export a hook definition`);
7201
+ }
7202
+ return null;
7203
+ }
7204
+ if (typeof loaded.id !== "string" || loaded.id !== hookId) {
7205
+ if (required) {
7206
+ throw new Error(`Required parent hook '${hookId}' exported a mismatched hook ID`);
7207
+ }
7208
+ return null;
7209
+ }
7210
+ if (typeof loaded.execute !== "function") {
7211
+ if (required) {
7212
+ throw new Error(`Required parent hook '${hookId}' did not export an execute function`);
7213
+ }
7214
+ return null;
7215
+ }
7216
+ if (USER_HOOK_NAMES.has(loaded.hook)) {
7217
+ if (required) {
7218
+ throw new Error(
7219
+ `Required parent hook '${hookId}' uses user-scoped hook type '${loaded.hook}'`
7220
+ );
7221
+ }
7222
+ return null;
7223
+ }
7224
+ if (!THREAD_HOOK_NAMES.has(loaded.hook)) {
7225
+ if (required) {
7226
+ throw new Error(`Required parent hook '${hookId}' exported invalid hook type '${loaded.hook}'`);
7227
+ }
7228
+ return null;
7229
+ }
7230
+ if (loaded.hook !== hookType) {
7231
+ return null;
7232
+ }
7233
+ return {
7234
+ id: hookId,
7235
+ hook: hookType,
7236
+ execute: loaded.execute,
7237
+ context
7238
+ };
7239
+ }
7240
+ async function resolveHooksForPrompt(input, hookType) {
7241
+ const agent = input.agent ?? await input.host.loadAgent(input.agentName);
7242
+ const agentPackage = packageOf(agent);
7243
+ const unqualifiedPromptName = input.promptName ?? agent.sideA?.prompt;
7244
+ const resolvedPromptName = unqualifiedPromptName ? qualifyWithinPackage(unqualifiedPromptName, agentPackage?.packageId) : void 0;
7245
+ const prompt = input.prompt ?? (resolvedPromptName ? await input.host.loadPrompt(resolvedPromptName) : void 0);
7246
+ const promptName = prompt?.name ?? prompt?.id ?? resolvedPromptName ?? "unknown_prompt";
7247
+ const before = [];
7248
+ const after = [];
7249
+ for (const entry of promptTools(prompt)) {
7250
+ const relation = relationDescriptor(entry);
7251
+ if (!relation) continue;
7252
+ if (relation.optional && input.isOptionalRelationshipEnabled && !await input.isOptionalRelationshipEnabled(relation.optional)) {
7253
+ continue;
7254
+ }
7255
+ const promptPackage = packageOf(prompt) ?? agentPackage;
7256
+ const childReference = qualifyWithinPackage(
7257
+ relation.implementingAgent,
7258
+ promptPackage?.packageId
7259
+ );
7260
+ let child;
7261
+ try {
7262
+ child = await input.host.loadAgent(childReference);
7263
+ } catch (error) {
7264
+ if (relation.explicitlyAgentShaped) throw error;
7265
+ continue;
7266
+ }
7267
+ if (child.type !== "dual_ai") continue;
7268
+ if (child.parentHooks !== void 0 && child.exposeAsTool !== true) {
7269
+ throw new Error(
7270
+ `Child agent '${childReference}' declares parentHooks but is not exposed as a tool`
7271
+ );
7272
+ }
7273
+ if (child.parentHooks !== void 0 && !Array.isArray(child.parentHooks)) {
7274
+ throw new Error(`Child agent '${childReference}' has an invalid parentHooks declaration`);
7275
+ }
7276
+ const requirements = child.parentHooks ?? [];
7277
+ const seenRequirements = /* @__PURE__ */ new Set();
7278
+ for (const requirement of requirements) {
7279
+ 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)) {
7280
+ throw new Error(`Child agent '${childReference}' has an invalid parentHooks requirement`);
7281
+ }
7282
+ seenRequirements.add(requirement.hook);
7283
+ const loaded = await loadBinding(
7284
+ input.host,
7285
+ child,
7286
+ requirement.hook,
7287
+ hookType,
7288
+ {
7289
+ source: {
7290
+ kind: "child",
7291
+ relationshipName: relation.relationshipName,
7292
+ agentName: childReference,
7293
+ package: packageOf(child)
7294
+ }
7295
+ },
7296
+ true
7297
+ );
7298
+ if (!loaded) continue;
7299
+ if ((requirement.stage ?? "after_parent") === "before_parent") {
7300
+ before.push(loaded);
7301
+ } else {
7302
+ after.push(loaded);
7303
+ }
7304
+ }
7305
+ }
7306
+ const promptHookIds = promptHooks(prompt);
7307
+ const promptOwnsBaseHooks = promptHookIds.length > 0 && prompt !== void 0;
7308
+ const baseOwner = promptOwnsBaseHooks ? prompt : agent;
7309
+ const baseIds = promptHookIds.length > 0 ? promptHookIds : Array.isArray(agent.hooks) ? agent.hooks : [];
7310
+ const baseContext = promptOwnsBaseHooks ? { source: { kind: "prompt", promptName } } : { source: { kind: "agent", agentName: input.agentName } };
7311
+ const base = [];
7312
+ for (const hookId of baseIds) {
7313
+ const loaded = await loadBinding(
7314
+ input.host,
7315
+ baseOwner,
7316
+ hookId,
7317
+ hookType,
7318
+ baseContext,
7319
+ false
7320
+ );
7321
+ if (loaded) base.push(loaded);
7322
+ }
7323
+ return [...before, ...base, ...after];
7324
+ }
7325
+ var USER_HOOK_NAMES, THREAD_HOOK_NAMES;
7326
+ var init_ParentHookResolver = __esm({
7327
+ "src/agents/ParentHookResolver.ts"() {
7328
+ init_resolver();
7329
+ USER_HOOK_NAMES = /* @__PURE__ */ new Set([
7330
+ "user_client_attached",
7331
+ "user_client_detached",
7332
+ "user_value_changed"
7333
+ ]);
7334
+ THREAD_HOOK_NAMES = /* @__PURE__ */ new Set([
7335
+ "after_thread_created",
7336
+ "after_subagent_created",
7337
+ "after_system_message",
7338
+ "filter_messages",
7339
+ "prefilter_llm_history",
7340
+ "before_create_message",
7341
+ "after_create_message",
7342
+ "before_update_message",
7343
+ "after_update_message",
7344
+ "before_store_tool_result",
7345
+ "after_tool_call_success",
7346
+ "after_tool_call_failure",
7347
+ "tool_call_started",
7348
+ "tool_call_done"
7349
+ ]);
7350
+ }
7351
+ });
7352
+
6809
7353
  // src/agents/FlowEngine.ts
6810
7354
  var FlowEngine_exports = {};
6811
7355
  __export(FlowEngine_exports, {
@@ -6822,6 +7366,7 @@ var init_FlowEngine = __esm({
6822
7366
  init_context();
6823
7367
  init_sessionTools();
6824
7368
  init_ThreadStateImpl();
7369
+ init_ParentHookResolver();
6825
7370
  IMMEDIATE_BOOTSTRAP_TOOL_NAME = "bootstrap_immediate_subagent";
6826
7371
  HookBlockedExecutionError = class extends Error {
6827
7372
  constructor(message) {
@@ -7164,7 +7709,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
7164
7709
  _variables: def.variables ?? [],
7165
7710
  _env: def.env ?? def.tenvs ?? {},
7166
7711
  _requiredSchema: def.requiredSchema ?? null,
7167
- _hooks: def.hooks ?? []
7712
+ _hooks: def.hooks ?? [],
7713
+ _package: def.__package
7168
7714
  };
7169
7715
  }
7170
7716
  /**
@@ -7554,6 +8100,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
7554
8100
  type: newAgentDef.type,
7555
8101
  created_at: Date.now(),
7556
8102
  max_session_turns: newAgentDef.maxSessionTurns ?? null,
8103
+ hooks: newAgentDef.hooks ?? [],
8104
+ _package: newAgentDef.__package,
7557
8105
  side_a_label: newAgentDef.sideA?.label ?? null,
7558
8106
  side_a_agent_prompt: newAgentDef.sideA?.prompt ?? null,
7559
8107
  side_a_stop_on_response: newAgentDef.sideA?.stopOnResponse ?? true,
@@ -7644,28 +8192,12 @@ This usually points to something I can't fix from here \u2014 for example the wo
7644
8192
  state.promptPath = [sideAPrompt.name];
7645
8193
  state.pendingImmediateAfterHandoff = true;
7646
8194
  if (userMessageContent || userMessageAttachments) {
7647
- const handoffMessageId = crypto.randomUUID();
7648
- const handoffTimestamp = Date.now() * 1e3;
7649
- const handoffMessage = {
7650
- id: handoffMessageId,
7651
- role: "user",
7652
- content: userMessageContent || "",
7653
- created_at: handoffTimestamp,
7654
- attachments: userMessageAttachments
7655
- };
7656
- await state.storage.sql.exec(
7657
- `INSERT INTO messages (id, role, content, attachments, created_at, prompt) VALUES (?, ?, ?, ?, ?, ?)`,
7658
- handoffMessage.id,
7659
- handoffMessage.role,
7660
- handoffMessage.content,
7661
- handoffMessage.attachments || null,
7662
- handoffMessage.created_at,
8195
+ await this.persistHandoffUserMessage(
8196
+ state,
8197
+ userMessageContent || "",
8198
+ userMessageAttachments,
7663
8199
  sideAPrompt.name
7664
8200
  );
7665
- state.messageHistory.push(handoffMessage);
7666
- if (state.emitMessage) {
7667
- state.emitMessage(handoffMessage);
7668
- }
7669
8201
  }
7670
8202
  state.pendingHandoff = void 0;
7671
8203
  state.emitTelemetry?.({
@@ -7680,6 +8212,37 @@ This usually points to something I can't fix from here \u2014 for example the wo
7680
8212
  throw error;
7681
8213
  }
7682
8214
  }
8215
+ /**
8216
+ * Persist the user message that rides an agent handoff, applying the
8217
+ * create-message lifecycle hooks like every other insertion path.
8218
+ */
8219
+ static async persistHandoffUserMessage(state, content, attachments, promptName) {
8220
+ let handoffMessage = {
8221
+ id: crypto.randomUUID(),
8222
+ role: "user",
8223
+ content,
8224
+ created_at: Date.now() * 1e3,
8225
+ status: "completed",
8226
+ attachments: attachments ?? null
8227
+ };
8228
+ handoffMessage = await this.runBeforeCreateMessageHook(state, handoffMessage);
8229
+ await state.storage.sql.exec(
8230
+ `INSERT INTO messages (id, role, content, attachments, created_at, status, silent, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
8231
+ handoffMessage.id,
8232
+ handoffMessage.role,
8233
+ handoffMessage.content,
8234
+ handoffMessage.attachments || null,
8235
+ handoffMessage.created_at,
8236
+ handoffMessage.status ?? "completed",
8237
+ handoffMessage.silent ? 1 : 0,
8238
+ promptName
8239
+ );
8240
+ state.messageHistory.push(handoffMessage);
8241
+ await this.runAfterCreateMessageHook(state, handoffMessage);
8242
+ if (state.emitMessage) {
8243
+ state.emitMessage(handoffMessage);
8244
+ }
8245
+ }
7683
8246
  static isEnabledEnvFlag(value) {
7684
8247
  if (value === null || value === void 0) return false;
7685
8248
  const normalized = value.trim().toLowerCase();
@@ -7699,7 +8262,7 @@ This usually points to something I can't fix from here \u2014 for example the wo
7699
8262
  return null;
7700
8263
  }
7701
8264
  return await stub.resolveThreadEnv({
7702
- threadId: state.threadId,
8265
+ threadId: state.thread.metadata.id,
7703
8266
  property,
7704
8267
  promptName: state.prompt?.name
7705
8268
  });
@@ -8221,8 +8784,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
8221
8784
  state.context[key] = existing;
8222
8785
  }
8223
8786
  static async runImmediateToolsIfNeeded(state) {
8224
- const promptTools = Array.isArray(state.prompt._tools) ? state.prompt._tools : [];
8225
- if (promptTools.length === 0) {
8787
+ const promptTools2 = Array.isArray(state.prompt._tools) ? state.prompt._tools : [];
8788
+ if (promptTools2.length === 0) {
8226
8789
  return false;
8227
8790
  }
8228
8791
  if (!state.immediatePromptRuns) {
@@ -8258,7 +8821,7 @@ This usually points to something I can't fix from here \u2014 for example the wo
8258
8821
  (activeInstanceCounts.get(child.name) ?? 0) + 1
8259
8822
  );
8260
8823
  }
8261
- for (const toolConfig of promptTools) {
8824
+ for (const toolConfig of promptTools2) {
8262
8825
  if (!toolConfig || typeof toolConfig !== "object") {
8263
8826
  continue;
8264
8827
  }
@@ -8424,10 +8987,10 @@ This usually points to something I can't fix from here \u2014 for example the wo
8424
8987
  * model's to clean up, and terminate_subagent on an infrastructure child
8425
8988
  * kills work the runtime depends on. Static and pure for direct testing.
8426
8989
  */
8427
- static filterModelVisibleSubagents(registry, promptTools) {
8990
+ static filterModelVisibleSubagents(registry, promptTools2) {
8428
8991
  if (registry.length === 0) return registry;
8429
8992
  const hidden = /* @__PURE__ */ new Set();
8430
- for (const config of promptTools ?? []) {
8993
+ for (const config of promptTools2 ?? []) {
8431
8994
  if (typeof config === "object" && config !== null && "name" in config && config.hidden === true) {
8432
8995
  hidden.add(config.name);
8433
8996
  }
@@ -8577,7 +9140,16 @@ This usually points to something I can't fix from here \u2014 for example the wo
8577
9140
  role: "system",
8578
9141
  content: systemPromptText
8579
9142
  });
8580
- const completedMessages = state.messageHistory.filter(
9143
+ const historySnapshot = state.messageHistory.filter(
9144
+ (msg) => msg.status !== "pending"
9145
+ );
9146
+ const filteredHistory = await this.runFilterMessagesHook(
9147
+ state.thread.instance,
9148
+ state,
9149
+ historySnapshot
9150
+ );
9151
+ const contextHistory = this.filterOrphanedToolCalls(filteredHistory);
9152
+ const completedMessages = contextHistory.filter(
8581
9153
  (msg) => msg.status !== "pending"
8582
9154
  );
8583
9155
  const recentMessageThreshold = state.prompt.recentImageThreshold ?? 10;
@@ -9448,8 +10020,8 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9448
10020
  try {
9449
10021
  const nativeTools = await this.loadNativeTools(state);
9450
10022
  tools.push(...nativeTools);
9451
- const promptTools = await this.loadPromptTools(state);
9452
- tools.push(...promptTools);
10023
+ const promptTools2 = await this.loadPromptTools(state);
10024
+ tools.push(...promptTools2);
9453
10025
  const agentTools = await this.loadAgentTools(state);
9454
10026
  tools.push(...agentTools);
9455
10027
  const toolNameSources = /* @__PURE__ */ new Map();
@@ -9458,7 +10030,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9458
10030
  sources.add("native tool");
9459
10031
  toolNameSources.set(tool.function.name, sources);
9460
10032
  }
9461
- for (const tool of promptTools) {
10033
+ for (const tool of promptTools2) {
9462
10034
  const sources = toolNameSources.get(tool.function.name) || /* @__PURE__ */ new Set();
9463
10035
  sources.add("prompt");
9464
10036
  toolNameSources.set(tool.function.name, sources);
@@ -9600,24 +10172,8 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9600
10172
  // Hook Resolution Helpers
9601
10173
  // ==========================================================================
9602
10174
  /**
9603
- * Get the list of hook IDs that should run for the current context.
9604
- * Resolution: prompt hooks > agent hooks > empty array
9605
- *
9606
- * @param state - Flow state with prompt and agent configuration
9607
- * @returns Array of hook IDs to execute
9608
- */
9609
- static getActiveHookIds(state) {
9610
- if (state.prompt?._hooks !== void 0 && state.prompt._hooks.length > 0) {
9611
- return state.prompt._hooks;
9612
- }
9613
- if (state.agentConfig.hooks !== void 0 && state.agentConfig.hooks.length > 0) {
9614
- return state.agentConfig.hooks;
9615
- }
9616
- return [];
9617
- }
9618
- /**
9619
- * Get hooks of a specific type from the active hook IDs.
9620
- * Returns typed hook executors in the order they appear in the hooks array.
10175
+ * Resolve hooks of a specific type for the active prompt, including required
10176
+ * hooks contributed automatically by its enabled direct child agents.
9621
10177
  *
9622
10178
  * @template K - The hook type name (ensures type-safe executors)
9623
10179
  * @param state - Flow state with hook configuration
@@ -9625,24 +10181,17 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9625
10181
  * @returns Array of typed hook executor functions
9626
10182
  */
9627
10183
  static async getHooksForType(state, hookType) {
9628
- const hookIds = this.getActiveHookIds(state);
9629
- if (hookIds.length === 0) return [];
9630
- const hooksRegistry = state.thread.instance.hooks();
9631
- if (!hooksRegistry) return [];
9632
- const executors = [];
9633
- for (const hookId of hookIds) {
9634
- const hookLoader = hooksRegistry[hookId];
9635
- if (!hookLoader) continue;
9636
- try {
9637
- const hookDef = await hookLoader();
9638
- if (hookDef && hookDef.hook === hookType) {
9639
- executors.push(hookDef.execute);
9640
- }
9641
- } catch (error) {
9642
- console.error(`[Hooks] Failed to load hook '${hookId}':`, error);
9643
- }
9644
- }
9645
- return executors;
10184
+ return await resolveHooksForPrompt(
10185
+ {
10186
+ host: state.thread.instance,
10187
+ agentName: state.agentConfig.id,
10188
+ agent: state.agentConfig,
10189
+ promptName: state.prompt?.id,
10190
+ prompt: state.prompt,
10191
+ isOptionalRelationshipEnabled: (envName) => this.isOptionalToolEnabled(state, envName)
10192
+ },
10193
+ hookType
10194
+ );
9646
10195
  }
9647
10196
  // ==========================================================================
9648
10197
  // Hook Runner Methods
@@ -9660,9 +10209,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9660
10209
  if (executors.length === 0) return messages;
9661
10210
  let result = messages;
9662
10211
  const threadState = ThreadStateImpl.fromFlowState(state);
9663
- for (const executor of executors) {
10212
+ for (const { execute, context } of executors) {
9664
10213
  try {
9665
- result = await executor(threadState, result);
10214
+ result = await execute(threadState, result, context);
9666
10215
  } catch (error) {
9667
10216
  if (error && typeof error === "object" && error.block === true) {
9668
10217
  throw new HookBlockedExecutionError(error instanceof Error ? error.message : String(error));
@@ -9680,9 +10229,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9680
10229
  if (executors.length === 0) return systemMessage;
9681
10230
  let result = systemMessage;
9682
10231
  const threadState = ThreadStateImpl.fromFlowState(state);
9683
- for (const executor of executors) {
10232
+ for (const { execute, context } of executors) {
9684
10233
  try {
9685
- const next = await executor(threadState, result);
10234
+ const next = await execute(threadState, result, context);
9686
10235
  if (next !== null && next !== void 0) {
9687
10236
  result = next;
9688
10237
  }
@@ -9699,9 +10248,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9699
10248
  const executors = await this.getHooksForType(state, "after_subagent_created");
9700
10249
  if (executors.length === 0) return;
9701
10250
  const threadState = ThreadStateImpl.fromFlowState(state);
9702
- for (const executor of executors) {
10251
+ for (const { execute, context } of executors) {
9703
10252
  try {
9704
- await executor(threadState, childState);
10253
+ await execute(threadState, childState, context);
9705
10254
  } catch (error) {
9706
10255
  console.error("[Hooks] \u2717 Error running after_subagent_created hook:", error);
9707
10256
  }
@@ -9715,20 +10264,27 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9715
10264
  * @param message - Message object to be created
9716
10265
  * @returns Processed message (or original if hook doesn't exist/fails)
9717
10266
  */
9718
- static async runBeforeCreateMessageHook(state, message) {
10267
+ static async runBeforeCreateMessageHook(state, message, threadStateOverride, options) {
9719
10268
  const executors = await this.getHooksForType(state, "before_create_message");
9720
10269
  if (executors.length === 0) return message;
9721
10270
  let result = message;
9722
- const threadState = ThreadStateImpl.fromFlowState(state);
9723
- for (const executor of executors) {
10271
+ const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
10272
+ for (const { execute, context } of executors) {
9724
10273
  try {
9725
- const hooked = await executor(threadState, _FlowEngine.rowToHookMessage(result));
10274
+ const hooked = await execute(
10275
+ threadState,
10276
+ _FlowEngine.rowToHookMessage(result),
10277
+ context
10278
+ );
9726
10279
  result = {
9727
10280
  ...result,
9728
10281
  ...hooked,
9729
10282
  attachments: hooked.attachments === void 0 ? result.attachments : hooked.attachments === null ? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments)
9730
10283
  };
9731
10284
  } catch (error) {
10285
+ if (options?.propagateHookErrors) {
10286
+ throw error;
10287
+ }
9732
10288
  console.error("[Hooks] \u2717 Error running before_create_message hook:", error);
9733
10289
  }
9734
10290
  }
@@ -9741,13 +10297,13 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9741
10297
  * @param state - Flow state with execution context
9742
10298
  * @param message - The created message object
9743
10299
  */
9744
- static async runAfterCreateMessageHook(state, message) {
10300
+ static async runAfterCreateMessageHook(state, message, threadStateOverride) {
9745
10301
  const executors = await this.getHooksForType(state, "after_create_message");
9746
10302
  if (executors.length === 0) return;
9747
- const threadState = ThreadStateImpl.fromFlowState(state);
9748
- for (const executor of executors) {
10303
+ const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
10304
+ for (const { execute, context } of executors) {
9749
10305
  try {
9750
- await executor(threadState, _FlowEngine.rowToHookMessage(message));
10306
+ await execute(threadState, _FlowEngine.rowToHookMessage(message), context);
9751
10307
  } catch (error) {
9752
10308
  console.error("[Hooks] \u2717 Error running after_create_message hook:", error);
9753
10309
  }
@@ -9788,9 +10344,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9788
10344
  if (executors.length === 0) return updates;
9789
10345
  let result = updates;
9790
10346
  const threadState = ThreadStateImpl.fromFlowState(state);
9791
- for (const executor of executors) {
10347
+ for (const { execute, context } of executors) {
9792
10348
  try {
9793
- result = await executor(threadState, messageId, result);
10349
+ result = await execute(threadState, messageId, result, context);
9794
10350
  } catch (error) {
9795
10351
  console.error("[Hooks] \u2717 Error running before_update_message hook:", error);
9796
10352
  }
@@ -9808,9 +10364,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9808
10364
  const executors = await this.getHooksForType(state, "after_update_message");
9809
10365
  if (executors.length === 0) return;
9810
10366
  const threadState = ThreadStateImpl.fromFlowState(state);
9811
- for (const executor of executors) {
10367
+ for (const { execute, context } of executors) {
9812
10368
  try {
9813
- await executor(threadState, _FlowEngine.rowToHookMessage(message));
10369
+ await execute(threadState, _FlowEngine.rowToHookMessage(message), context);
9814
10370
  } catch (error) {
9815
10371
  console.error("[Hooks] \u2717 Error running after_update_message hook:", error);
9816
10372
  }
@@ -9830,9 +10386,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9830
10386
  if (executors.length === 0) return toolResult;
9831
10387
  let result = toolResult;
9832
10388
  const threadState = ThreadStateImpl.fromFlowState(state);
9833
- for (const executor of executors) {
10389
+ for (const { execute, context } of executors) {
9834
10390
  try {
9835
- result = await executor(threadState, toolCall, result);
10391
+ result = await execute(threadState, toolCall, result, context);
9836
10392
  } catch (error) {
9837
10393
  console.error("[Hooks] \u2717 Error running before_store_tool_result hook:", error);
9838
10394
  }
@@ -9852,10 +10408,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9852
10408
  if (executors.length === 0) return toolResult;
9853
10409
  let result = toolResult;
9854
10410
  const threadState = ThreadStateImpl.fromFlowState(state);
9855
- for (const executor of executors) {
10411
+ for (const { execute, context } of executors) {
9856
10412
  if (result === null) break;
9857
10413
  try {
9858
- result = await executor(threadState, toolCall, result);
10414
+ result = await execute(threadState, toolCall, result, context);
9859
10415
  } catch (error) {
9860
10416
  console.error("[Hooks] \u2717 Error running after_tool_call_success hook:", error);
9861
10417
  }
@@ -9875,10 +10431,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9875
10431
  if (executors.length === 0) return toolResult;
9876
10432
  let result = toolResult;
9877
10433
  const threadState = ThreadStateImpl.fromFlowState(state);
9878
- for (const executor of executors) {
10434
+ for (const { execute, context } of executors) {
9879
10435
  if (result === null) break;
9880
10436
  try {
9881
- result = await executor(threadState, toolCall, result);
10437
+ result = await execute(threadState, toolCall, result, context);
9882
10438
  } catch (error) {
9883
10439
  console.error("[Hooks] \u2717 Error running after_tool_call_failure hook:", error);
9884
10440
  }
@@ -9908,9 +10464,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9908
10464
  const executors = await this.getHooksForType(state, "tool_call_started");
9909
10465
  if (executors.length === 0) return;
9910
10466
  const threadState = ThreadStateImpl.fromFlowState(state);
9911
- for (const executor of executors) {
10467
+ for (const { execute, context } of executors) {
9912
10468
  try {
9913
- await executor(threadState, toolCall, progress);
10469
+ await execute(threadState, toolCall, progress, context);
9914
10470
  } catch (error) {
9915
10471
  console.error("[Hooks] \u2717 Error running tool_call_started hook:", error);
9916
10472
  }
@@ -9938,9 +10494,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9938
10494
  const executors = await this.getHooksForType(state, "tool_call_done");
9939
10495
  if (executors.length === 0) return;
9940
10496
  const threadState = ThreadStateImpl.fromFlowState(state);
9941
- for (const executor of executors) {
10497
+ for (const { execute, context } of executors) {
9942
10498
  try {
9943
- await executor(threadState, toolCall, toolResult);
10499
+ await execute(threadState, toolCall, toolResult, context);
9944
10500
  } catch (error) {
9945
10501
  console.error("[Hooks] \u2717 Error running tool_call_done hook:", error);
9946
10502
  }
@@ -10245,7 +10801,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10245
10801
  }
10246
10802
  existingPathMessages.add(content);
10247
10803
  const attachmentsJson = JSON.stringify([ref]);
10248
- const silentMessage = {
10804
+ let silentMessage = {
10249
10805
  id: crypto.randomUUID(),
10250
10806
  role: sourceMessage.role,
10251
10807
  content,
@@ -10256,6 +10812,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10256
10812
  parent_id: parentId,
10257
10813
  depth
10258
10814
  };
10815
+ silentMessage = await this.runBeforeCreateMessageHook(state, silentMessage);
10259
10816
  await state.storage.sql.exec(
10260
10817
  `
10261
10818
  INSERT INTO messages (
@@ -10266,15 +10823,16 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10266
10823
  silentMessage.id,
10267
10824
  silentMessage.role,
10268
10825
  silentMessage.content,
10269
- attachmentsJson,
10270
- 1,
10826
+ silentMessage.attachments ?? null,
10827
+ silentMessage.silent ? 1 : 0,
10271
10828
  silentMessage.created_at,
10272
- silentMessage.status,
10829
+ silentMessage.status ?? "completed",
10273
10830
  silentMessage.parent_id,
10274
10831
  silentMessage.depth,
10275
10832
  currentPrompt
10276
10833
  );
10277
10834
  state.messageHistory.push(silentMessage);
10835
+ await this.runAfterCreateMessageHook(state, silentMessage);
10278
10836
  if (state.emitMessage) {
10279
10837
  state.emitMessage(silentMessage);
10280
10838
  }
@@ -10813,7 +11371,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10813
11371
  */
10814
11372
  static async loadMessageHistory(state) {
10815
11373
  const storage = state.storage;
10816
- const thread = state.thread;
11374
+ state.thread;
10817
11375
  const includeParentHistory = state.prompt.include_chat ?? true;
10818
11376
  const includeToolCalls = state.prompt.include_past_tools ?? true;
10819
11377
  try {
@@ -10849,8 +11407,11 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10849
11407
  rows = cursor.toArray();
10850
11408
  }
10851
11409
  if (state.extraMessages && state.extraMessages.length > 0) {
10852
- rows.push(...state.extraMessages);
10853
- rows.sort((a, b) => a.created_at - b.created_at || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
11410
+ const persistedIds = new Set(rows.map((row) => row.id));
11411
+ rows.push(
11412
+ ...state.extraMessages.filter((entry) => !persistedIds.has(entry.id))
11413
+ );
11414
+ rows.sort((a, b) => a.created_at - b.created_at);
10854
11415
  }
10855
11416
  const messages = rows.map((row) => ({
10856
11417
  id: row.id,
@@ -10873,43 +11434,77 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10873
11434
  reasoning_content: row.reasoning_content,
10874
11435
  reasoning_details: row.reasoning_details
10875
11436
  }));
10876
- return this.runFilterMessagesHook(
10877
- thread.instance,
10878
- state,
10879
- this.filterOrphanedToolCalls(messages)
10880
- );
11437
+ return messages;
10881
11438
  } catch (error) {
10882
11439
  console.error("Error loading message history:", error);
10883
11440
  return [];
10884
11441
  }
10885
11442
  }
10886
11443
  /**
10887
- * Run the filter_messages hook if it exists
10888
- * This hook allows developers to modify SQL row data before transformation
10889
- *
10890
- * @param thread - Thread instance with hooks registry
10891
- * @param state - Flow state for hook context
10892
- * @param rows - SQL rows from messages table
10893
- * @returns Processed rows (or original if hook doesn't exist/fails)
11444
+ * Build request-context rows from persisted history.
11445
+ * Mutations and synthetic messages stay in the request context.
10894
11446
  */
10895
11447
  static async runFilterMessagesHook(thread, state, messages) {
10896
11448
  const executors = await this.getHooksForType(state, "filter_messages");
10897
11449
  if (executors.length === 0) return messages;
10898
11450
  let result = messages;
10899
11451
  const threadState = ThreadStateImpl.fromFlowState(state);
10900
- for (const executor of executors) {
11452
+ for (const { execute, context } of executors) {
10901
11453
  try {
10902
11454
  const byId = /* @__PURE__ */ new Map();
10903
11455
  for (const row of result) if (row.id) byId.set(row.id, row);
10904
11456
  const hookView = result.map((row) => _FlowEngine.rowToHookMessage(row));
10905
- const kept = await executor(threadState, hookView);
10906
- result = kept.map((m) => m.id && byId.get(m.id) || null).filter((row) => row !== null);
11457
+ const kept = await execute(threadState, hookView, context);
11458
+ result = kept.map((hookedMessage) => {
11459
+ const original = hookedMessage.id ? byId.get(hookedMessage.id) : void 0;
11460
+ if (original) {
11461
+ return _FlowEngine.applyHookMessageToRow(original, hookedMessage);
11462
+ }
11463
+ return _FlowEngine.hookMessageToContextRow(hookedMessage);
11464
+ });
10907
11465
  } catch (error) {
10908
11466
  console.error("[Hooks] \u2717 Error running filter_messages hook:", error);
10909
11467
  }
10910
11468
  }
10911
11469
  return result;
10912
11470
  }
11471
+ /**
11472
+ * Copy a persisted row and apply hook edits. The original live-history
11473
+ * object is left unchanged.
11474
+ */
11475
+ static applyHookMessageToRow(original, hooked) {
11476
+ const attachments = hooked.attachments === void 0 ? original.attachments : hooked.attachments === null ? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments);
11477
+ return {
11478
+ ...original,
11479
+ role: hooked.role ?? original.role,
11480
+ content: hooked.content !== void 0 ? hooked.content : original.content,
11481
+ name: hooked.name !== void 0 ? hooked.name : original.name,
11482
+ tool_calls: hooked.tool_calls !== void 0 ? hooked.tool_calls : original.tool_calls,
11483
+ tool_call_id: hooked.tool_call_id !== void 0 ? hooked.tool_call_id : original.tool_call_id,
11484
+ silent: hooked.silent !== void 0 ? hooked.silent : original.silent,
11485
+ attachments
11486
+ };
11487
+ }
11488
+ /**
11489
+ * Convert a synthetic hook message into a request-context-only builder row.
11490
+ */
11491
+ static hookMessageToContextRow(hooked) {
11492
+ const attachments = hooked.attachments === void 0 || hooked.attachments === null ? hooked.attachments ?? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments);
11493
+ return {
11494
+ id: hooked.id || crypto.randomUUID(),
11495
+ role: hooked.role,
11496
+ content: hooked.content ?? null,
11497
+ name: hooked.name ?? null,
11498
+ tool_calls: typeof hooked.tool_calls === "string" ? hooked.tool_calls : null,
11499
+ tool_call_id: hooked.tool_call_id ?? null,
11500
+ created_at: hooked.created_at ?? Date.now() * TIMESTAMP_MULTIPLIER,
11501
+ parent_id: hooked.parent_id ?? null,
11502
+ depth: hooked.depth ?? 0,
11503
+ silent: hooked.silent ?? false,
11504
+ status: "completed",
11505
+ attachments
11506
+ };
11507
+ }
10913
11508
  /**
10914
11509
  * Log an error to the logs table (best-effort)
10915
11510
  * Creates a log entry with error details but no associated message_id
@@ -11053,6 +11648,35 @@ ${errorStack}` : ""}`;
11053
11648
  });
11054
11649
 
11055
11650
  // src/agents/utilities.ts
11651
+ var utilities_exports = {};
11652
+ __export(utilities_exports, {
11653
+ cat: () => cat,
11654
+ emitThreadEvent: () => emitThreadEvent,
11655
+ exists: () => exists,
11656
+ find: () => find,
11657
+ forceTurn: () => forceTurn,
11658
+ getFileStats: () => getFileStats,
11659
+ getMessages: () => getMessages,
11660
+ getThumbnail: () => getThumbnail,
11661
+ grep: () => grep,
11662
+ head: () => head,
11663
+ injectMessage: () => injectMessage,
11664
+ linkFile: () => linkFile,
11665
+ mkdir: () => mkdir,
11666
+ move: () => move,
11667
+ queueTool: () => queueTool,
11668
+ readFile: () => readFile,
11669
+ readdir: () => readdir,
11670
+ reloadHistory: () => reloadHistory,
11671
+ rename: () => rename,
11672
+ rmdir: () => rmdir,
11673
+ stat: () => stat,
11674
+ tail: () => tail,
11675
+ unlink: () => unlink,
11676
+ updateThread: () => updateThread,
11677
+ writeFile: () => writeFile,
11678
+ writeImage: () => writeImage
11679
+ });
11056
11680
  function queueTool(flow, toolName, args = {}) {
11057
11681
  if (flow.currentToolUses !== void 0) {
11058
11682
  if (!flow.currentToolUses.includes(toolName)) {
@@ -11080,7 +11704,7 @@ async function injectMessage(flow, options) {
11080
11704
  const timestamp = Date.now() * TIMESTAMP_MULTIPLIER;
11081
11705
  const silent = options.silent ?? false;
11082
11706
  const forceTopLevel = options.forceTopLevel ?? false;
11083
- const message = {
11707
+ let message = {
11084
11708
  id: messageId,
11085
11709
  role: options.role,
11086
11710
  content: options.content,
@@ -11094,41 +11718,52 @@ async function injectMessage(flow, options) {
11094
11718
  metadata: options.metadata
11095
11719
  };
11096
11720
  if (options.beforeMessageId) {
11097
- const cursor = await flow.storage.sql.exec(
11098
- `SELECT created_at FROM messages WHERE id = ?`,
11099
- options.beforeMessageId
11721
+ const liveTarget = flow.messageHistory.find(
11722
+ (entry) => entry.id === options.beforeMessageId
11100
11723
  );
11101
- const rows = cursor.toArray();
11102
- const targetMessage = rows[0];
11103
- if (!targetMessage) {
11104
- throw new Error(
11105
- `Message with id ${options.beforeMessageId} not found`
11724
+ let targetCreatedAt = liveTarget?.created_at;
11725
+ if (targetCreatedAt === void 0) {
11726
+ const cursor = await flow.storage.sql.exec(
11727
+ `SELECT created_at FROM messages WHERE id = ?`,
11728
+ options.beforeMessageId
11106
11729
  );
11730
+ const rows = cursor.toArray();
11731
+ const targetMessage = rows[0];
11732
+ if (!targetMessage) {
11733
+ throw new Error(
11734
+ `Message with id ${options.beforeMessageId} not found`
11735
+ );
11736
+ }
11737
+ targetCreatedAt = targetMessage.created_at;
11107
11738
  }
11108
- message.created_at = targetMessage.created_at - 1;
11739
+ message.created_at = targetCreatedAt - 1;
11109
11740
  }
11110
11741
  const currentPrompt = flow.promptPath.length > 0 ? flow.promptPath[flow.promptPath.length - 1] : null;
11111
- const metadataJson = options.metadata ? JSON.stringify(options.metadata) : null;
11742
+ const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
11743
+ message = await FlowEngine2.runBeforeCreateMessageHook(flow, message, void 0, {
11744
+ propagateHookErrors: true
11745
+ });
11746
+ const metadataJson = message.metadata === void 0 ? null : JSON.stringify(message.metadata);
11112
11747
  await flow.storage.sql.exec(
11113
11748
  `INSERT INTO messages (id, role, content, name, tool_call_id, created_at, status, silent, parent_id, depth, metadata, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
11114
- messageId,
11749
+ message.id,
11115
11750
  message.role,
11116
11751
  message.content,
11117
11752
  message.name,
11118
11753
  message.tool_call_id,
11119
11754
  message.created_at,
11120
11755
  message.status,
11121
- silent ? 1 : 0,
11756
+ message.silent ? 1 : 0,
11122
11757
  message.parent_id,
11123
11758
  message.depth,
11124
11759
  metadataJson,
11125
11760
  currentPrompt
11126
11761
  );
11127
- if (!silent) {
11128
- flow.messageHistory.push(message);
11129
- } else {
11130
- flow.messageHistory = await reloadHistory(flow);
11762
+ appendLiveExecutionHistory(flow, message, options.beforeMessageId);
11763
+ if (flow.prompt.include_chat === false || message.parent_id !== null || (message.depth ?? 0) > 0) {
11764
+ appendInjectedContextMessage(flow.extraMessages, message);
11131
11765
  }
11766
+ await FlowEngine2.runAfterCreateMessageHook(flow, message);
11132
11767
  if (flow.emitMessage) {
11133
11768
  flow.emitMessage(message);
11134
11769
  }
@@ -11137,6 +11772,42 @@ async function injectMessage(flow, options) {
11137
11772
  }
11138
11773
  return message;
11139
11774
  }
11775
+ function appendLiveExecutionHistory(flow, message, beforeMessageId) {
11776
+ const history = flow.messageHistory;
11777
+ const existing = history.findIndex((entry) => entry.id === message.id);
11778
+ if (existing >= 0) {
11779
+ history.splice(existing, 1);
11780
+ }
11781
+ if (beforeMessageId) {
11782
+ const targetIndex = history.findIndex((entry) => entry.id === beforeMessageId);
11783
+ if (targetIndex >= 0) {
11784
+ history.splice(targetIndex, 0, message);
11785
+ return;
11786
+ }
11787
+ }
11788
+ const insertAt = history.findIndex(
11789
+ (entry) => entry.created_at > message.created_at
11790
+ );
11791
+ if (insertAt === -1) {
11792
+ history.push(message);
11793
+ return;
11794
+ }
11795
+ history.splice(insertAt, 0, message);
11796
+ }
11797
+ function appendInjectedContextMessage(extraMessages, message) {
11798
+ const existing = extraMessages.findIndex((entry) => entry.id === message.id);
11799
+ if (existing >= 0) {
11800
+ extraMessages.splice(existing, 1);
11801
+ }
11802
+ const insertAt = extraMessages.findIndex(
11803
+ (entry) => entry.created_at > message.created_at
11804
+ );
11805
+ if (insertAt === -1) {
11806
+ extraMessages.push(message);
11807
+ return;
11808
+ }
11809
+ extraMessages.splice(insertAt, 0, message);
11810
+ }
11140
11811
  async function getMessages(flow, limit = 100, offset = 0, order = "asc") {
11141
11812
  const orderClause = order === "desc" ? "DESC" : "ASC";
11142
11813
  const cursor = await flow.storage.sql.exec(
@@ -16868,6 +17539,10 @@ function generateAgentFile(data) {
16868
17539
  if (data.toolDescription) {
16869
17540
  lines.push(` toolDescription: '${escapeString3(data.toolDescription)}',`);
16870
17541
  }
17542
+ if (data.parentHooks && data.parentHooks.length > 0) {
17543
+ const serialized = JSON.stringify(data.parentHooks, null, 2).split("\n").map((line, index) => index === 0 ? line : ` ${line}`).join("\n");
17544
+ lines.push(` parentHooks: ${serialized},`);
17545
+ }
16871
17546
  const env = data.env ?? (data.tenvs || void 0);
16872
17547
  if (env && Object.keys(env).length > 0) {
16873
17548
  lines.push(` env: ${JSON.stringify(env)},`);
@@ -17637,6 +18312,9 @@ function transformAgentData(data) {
17637
18312
  if (data.tool_description) {
17638
18313
  transformed.toolDescription = data.tool_description;
17639
18314
  }
18315
+ if (data.parent_hooks !== void 0 || data.parentHooks !== void 0) {
18316
+ transformed.parentHooks = data.parent_hooks ?? data.parentHooks;
18317
+ }
17640
18318
  if (data.env !== void 0 || data.tenvs !== void 0) {
17641
18319
  transformed.env = normalizeStringEnvRecord(data.env ?? data.tenvs) ?? {};
17642
18320
  }
@@ -17882,6 +18560,22 @@ function validateAgentData(data) {
17882
18560
  if (data.exposeAsTool && !data.toolDescription) {
17883
18561
  errors["tool_description"] = "Tool description is required when expose as tool is enabled";
17884
18562
  }
18563
+ if (data.parentHooks !== void 0) {
18564
+ if (!Array.isArray(data.parentHooks)) {
18565
+ errors["parent_hooks"] = "Parent hooks must be an array";
18566
+ } else if (data.type !== "dual_ai" || !data.exposeAsTool) {
18567
+ errors["parent_hooks"] = "Parent hooks require an exposed dual_ai agent";
18568
+ } else {
18569
+ const seen = /* @__PURE__ */ new Set();
18570
+ for (const requirement of data.parentHooks) {
18571
+ 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" || seen.has(requirement.hook)) {
18572
+ errors["parent_hooks"] = "Parent hook entries require a unique hook, reason, and valid stage";
18573
+ break;
18574
+ }
18575
+ seen.add(requirement.hook);
18576
+ }
18577
+ }
18578
+ }
17885
18579
  if (data.maxSessionTurns !== void 0 && data.maxSessionTurns !== null) {
17886
18580
  if (typeof data.maxSessionTurns !== "number" || data.maxSessionTurns <= 0) {
17887
18581
  errors["max_session_turns"] = "Max session turns must be a positive number";
@@ -18027,6 +18721,44 @@ function discoverPackedPackageDirectories(packedDir) {
18027
18721
  }
18028
18722
  return packageDirs;
18029
18723
  }
18724
+ function discoverStandardAgentPackageDirectories(rootDir) {
18725
+ const packages = /* @__PURE__ */ new Map();
18726
+ const addPackage = (packagePath, source) => {
18727
+ const packageJsonPath = path9__default.join(packagePath, "package.json");
18728
+ if (!fs9__default.existsSync(packageJsonPath)) return;
18729
+ try {
18730
+ const packageJson = JSON.parse(fs9__default.readFileSync(packageJsonPath, "utf8"));
18731
+ if (typeof packageJson.name !== "string" || !Array.isArray(packageJson.standardagent?.entryAgents)) {
18732
+ return;
18733
+ }
18734
+ packages.set(packageJson.name, { path: packagePath, source });
18735
+ } catch {
18736
+ }
18737
+ };
18738
+ const nodeModulesDir = path9__default.join(rootDir, "node_modules");
18739
+ if (fs9__default.existsSync(nodeModulesDir)) {
18740
+ for (const entry of fs9__default.readdirSync(nodeModulesDir, { withFileTypes: true })) {
18741
+ if (entry.name.startsWith(".")) continue;
18742
+ const entryPath = path9__default.join(nodeModulesDir, entry.name);
18743
+ if (entry.name.startsWith("@")) {
18744
+ if (!fs9__default.existsSync(entryPath)) continue;
18745
+ for (const scopedEntry of fs9__default.readdirSync(entryPath, { withFileTypes: true })) {
18746
+ if (!scopedEntry.name.startsWith(".")) {
18747
+ addPackage(path9__default.join(entryPath, scopedEntry.name), "npm");
18748
+ }
18749
+ }
18750
+ } else {
18751
+ addPackage(entryPath, "npm");
18752
+ }
18753
+ }
18754
+ }
18755
+ for (const packagePath of discoverPackedPackageDirectories(
18756
+ path9__default.join(rootDir, "agents", "packed")
18757
+ )) {
18758
+ addPackage(packagePath, "local");
18759
+ }
18760
+ return [...packages.values()];
18761
+ }
18030
18762
  function scanPackedDistDirectory(packagePath, outputDir, subDir) {
18031
18763
  const fullPath = path9__default.join(packagePath, outputDir, subDir);
18032
18764
  if (!fs9__default.existsSync(fullPath)) {
@@ -18970,11 +19702,19 @@ async function extractPromptTools(sourceCode) {
18970
19702
  result.tools.push(element.text);
18971
19703
  } else if (ts.isObjectLiteralExpression(element)) {
18972
19704
  result.hasSubpromptConfigs = true;
19705
+ let configuredName;
19706
+ let implementingAgent;
18973
19707
  for (const objProp of element.properties) {
18974
- if (ts.isPropertyAssignment(objProp) && ts.isIdentifier(objProp.name) && objProp.name.text === "name" && ts.isStringLiteral(objProp.initializer)) {
18975
- result.tools.push(objProp.initializer.text);
19708
+ if (ts.isPropertyAssignment(objProp) && ts.isIdentifier(objProp.name) && ts.isStringLiteral(objProp.initializer)) {
19709
+ if (objProp.name.text === "name") {
19710
+ configuredName = objProp.initializer.text;
19711
+ } else if (objProp.name.text === "agent") {
19712
+ implementingAgent = objProp.initializer.text;
19713
+ }
18976
19714
  }
18977
19715
  }
19716
+ const dependencyName = implementingAgent ?? configuredName;
19717
+ if (dependencyName) result.tools.push(dependencyName);
18978
19718
  }
18979
19719
  }
18980
19720
  }
@@ -19097,6 +19837,106 @@ async function extractAgentPrompts(sourceCode) {
19097
19837
  visit(sourceFile);
19098
19838
  return result;
19099
19839
  }
19840
+ async function extractAgentHooks(sourceCode) {
19841
+ const ts = await loadTypeScript();
19842
+ const sourceFile = ts.createSourceFile(
19843
+ "agent.ts",
19844
+ sourceCode,
19845
+ ts.ScriptTarget.ESNext,
19846
+ true
19847
+ );
19848
+ const arrays = /* @__PURE__ */ new Map();
19849
+ const result = {
19850
+ hooks: [],
19851
+ parentHooks: [],
19852
+ parentHookRequirements: []
19853
+ };
19854
+ const unwrapArray = (expression) => {
19855
+ let current = expression;
19856
+ while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isSatisfiesExpression(current)) {
19857
+ current = current.expression;
19858
+ }
19859
+ if (ts.isArrayLiteralExpression(current)) return current;
19860
+ if (ts.isIdentifier(current)) return arrays.get(current.text) ?? null;
19861
+ return null;
19862
+ };
19863
+ for (const statement of sourceFile.statements) {
19864
+ if (!ts.isVariableStatement(statement)) continue;
19865
+ for (const declaration of statement.declarationList.declarations) {
19866
+ if (ts.isIdentifier(declaration.name) && declaration.initializer) {
19867
+ const array = unwrapArray(declaration.initializer);
19868
+ if (array) arrays.set(declaration.name.text, array);
19869
+ }
19870
+ }
19871
+ }
19872
+ const pushUnique = (target, value) => {
19873
+ if (!target.includes(value)) target.push(value);
19874
+ };
19875
+ function visit(node) {
19876
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "defineAgent" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
19877
+ for (const property of node.arguments[0].properties) {
19878
+ if (!ts.isPropertyAssignment(property) || !ts.isIdentifier(property.name)) continue;
19879
+ const array = unwrapArray(property.initializer);
19880
+ if (!array) continue;
19881
+ if (property.name.text === "hooks") {
19882
+ for (const element of array.elements) {
19883
+ if (ts.isStringLiteral(element)) pushUnique(result.hooks, element.text);
19884
+ }
19885
+ }
19886
+ if (property.name.text === "parentHooks") {
19887
+ for (const element of array.elements) {
19888
+ if (!ts.isObjectLiteralExpression(element)) continue;
19889
+ let hook;
19890
+ let reason;
19891
+ let stage;
19892
+ for (const requirement of element.properties) {
19893
+ if (ts.isPropertyAssignment(requirement) && ts.isIdentifier(requirement.name) && ts.isStringLiteral(requirement.initializer)) {
19894
+ if (requirement.name.text === "hook") hook = requirement.initializer.text;
19895
+ if (requirement.name.text === "reason") reason = requirement.initializer.text;
19896
+ if (requirement.name.text === "stage" && (requirement.initializer.text === "before_parent" || requirement.initializer.text === "after_parent")) {
19897
+ stage = requirement.initializer.text;
19898
+ }
19899
+ }
19900
+ }
19901
+ if (hook) {
19902
+ pushUnique(result.parentHooks, hook);
19903
+ if (reason) result.parentHookRequirements.push({ hook, reason, stage });
19904
+ }
19905
+ }
19906
+ }
19907
+ }
19908
+ }
19909
+ ts.forEachChild(node, visit);
19910
+ }
19911
+ visit(sourceFile);
19912
+ return result;
19913
+ }
19914
+ async function extractPromptHooks(sourceCode) {
19915
+ const ts = await loadTypeScript();
19916
+ const sourceFile = ts.createSourceFile(
19917
+ "prompt.ts",
19918
+ sourceCode,
19919
+ ts.ScriptTarget.ESNext,
19920
+ true
19921
+ );
19922
+ const hooks = [];
19923
+ function visit(node) {
19924
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "definePrompt" && node.arguments.length > 0 && ts.isObjectLiteralExpression(node.arguments[0])) {
19925
+ for (const property of node.arguments[0].properties) {
19926
+ if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.text === "hooks" && ts.isArrayLiteralExpression(property.initializer)) {
19927
+ for (const element of property.initializer.elements) {
19928
+ if (ts.isStringLiteral(element) && !hooks.includes(element.text)) {
19929
+ hooks.push(element.text);
19930
+ }
19931
+ }
19932
+ }
19933
+ }
19934
+ }
19935
+ ts.forEachChild(node, visit);
19936
+ }
19937
+ visit(sourceFile);
19938
+ return hooks;
19939
+ }
19100
19940
  async function extractAgentDescription(sourceCode) {
19101
19941
  const ts = await loadTypeScript();
19102
19942
  const sourceFile = ts.createSourceFile(
@@ -19336,6 +20176,13 @@ var PackingService = class {
19336
20176
  });
19337
20177
  const agentSource = fs9.readFileSync(agentFilePath, "utf-8");
19338
20178
  const agentPrompts = await extractAgentPrompts(agentSource);
20179
+ const agentHooks = await extractAgentHooks(agentSource);
20180
+ for (const hookId of agentHooks.hooks) {
20181
+ await this.analyzeHook(hookId, agentsDir, analysis, false, `agent:${agentName}`);
20182
+ }
20183
+ for (const hookId of agentHooks.parentHooks) {
20184
+ await this.analyzeHook(hookId, agentsDir, analysis, true, `agent:${agentName}`);
20185
+ }
19339
20186
  if (agentPrompts.sideA) {
19340
20187
  analysis.primaryPrompt = agentPrompts.sideA;
19341
20188
  await this.analyzePrompt(agentPrompts.sideA, agentsDir, analysis, /* @__PURE__ */ new Set(), `agent:${agentName}`);
@@ -19538,6 +20385,9 @@ var PackingService = class {
19538
20385
  }
19539
20386
  visited.add(`prompt:${promptName}`);
19540
20387
  const promptSource = fs9.readFileSync(promptFilePath, "utf-8");
20388
+ for (const hookId of await extractPromptHooks(promptSource)) {
20389
+ await this.analyzeHook(hookId, agentsDir, analysis, false, thisKey);
20390
+ }
19541
20391
  const modelName = await extractPromptModel(promptSource);
19542
20392
  if (modelName) {
19543
20393
  await this.analyzeModel(modelName, agentsDir, analysis, visited, thisKey);
@@ -19622,6 +20472,13 @@ var PackingService = class {
19622
20472
  visited.add(`agent:${agentName}`);
19623
20473
  const agentSource = fs9.readFileSync(agentFilePath, "utf-8");
19624
20474
  const agentPrompts = await extractAgentPrompts(agentSource);
20475
+ const agentHooks = await extractAgentHooks(agentSource);
20476
+ for (const hookId of agentHooks.hooks) {
20477
+ await this.analyzeHook(hookId, agentsDir, analysis, false, thisKey);
20478
+ }
20479
+ for (const hookId of agentHooks.parentHooks) {
20480
+ await this.analyzeHook(hookId, agentsDir, analysis, true, thisKey);
20481
+ }
19625
20482
  if (agentPrompts.sideA) {
19626
20483
  await this.analyzePrompt(agentPrompts.sideA, agentsDir, analysis, visited, thisKey);
19627
20484
  }
@@ -19629,6 +20486,27 @@ var PackingService = class {
19629
20486
  await this.analyzePrompt(agentPrompts.sideB, agentsDir, analysis, visited, thisKey);
19630
20487
  }
19631
20488
  }
20489
+ /** Add a hook referenced by an agent or prompt to the packed graph. */
20490
+ async analyzeHook(hookId, agentsDir, analysis, required, parentKey) {
20491
+ const hookFilePath = await this.findFile(path9.join(agentsDir, "hooks"), hookId);
20492
+ if (!hookFilePath) {
20493
+ const message = `Hook file not found for ID: ${hookId}`;
20494
+ if (required) analysis.errors.push(message);
20495
+ else analysis.warnings.push(message);
20496
+ return;
20497
+ }
20498
+ if (!analysis.constituents.hooks.some(
20499
+ (hook) => hook.name === hookId && hook.parentKey === parentKey
20500
+ )) {
20501
+ analysis.constituents.hooks.push({
20502
+ name: hookId,
20503
+ filePath: hookFilePath,
20504
+ discoveredVia: "static",
20505
+ sharedWith: [],
20506
+ parentKey
20507
+ });
20508
+ }
20509
+ }
19632
20510
  /**
19633
20511
  * Analyze a model and its fallbacks.
19634
20512
  */
@@ -19763,11 +20641,17 @@ var PackingService = class {
19763
20641
  } = options;
19764
20642
  const finalPackageName = packageName || packageId;
19765
20643
  const packedAt = Date.now();
20644
+ const entryAgentFile = await this.findFile(
20645
+ path9.join(rootDir, "agents", "agents"),
20646
+ agentName
20647
+ );
20648
+ const entryParentHooks = entryAgentFile ? (await extractAgentHooks(fs9.readFileSync(entryAgentFile, "utf-8"))).parentHookRequirements : [];
19766
20649
  const meta = {
19767
20650
  packageId,
19768
20651
  version,
19769
20652
  entryAgents: [agentName],
19770
- packedAt
20653
+ packedAt,
20654
+ ...entryParentHooks.length > 0 ? { parentHooks: { [agentName]: entryParentHooks } } : {}
19771
20655
  };
19772
20656
  const result = {
19773
20657
  success: false,
@@ -19876,6 +20760,7 @@ var PackingService = class {
19876
20760
  agentName,
19877
20761
  this.deduplicateConstituents(analysis).effects.map((effect) => effect.name),
19878
20762
  this.deduplicateConstituents(analysis).threadEndpoints.map((endpoint) => endpoint.name),
20763
+ meta.parentHooks,
19879
20764
  externalDeps,
19880
20765
  rootDir,
19881
20766
  license,
@@ -20267,7 +21152,7 @@ var PackingService = class {
20267
21152
  /**
20268
21153
  * Generate the package.json file with dependencies.
20269
21154
  */
20270
- generatePackageJson(packageName, version, agentName, effects, threadEndpoints, externalDeps, rootDir, license, licenseOwner) {
21155
+ generatePackageJson(packageName, version, agentName, effects, threadEndpoints, parentHooks, externalDeps, rootDir, license, licenseOwner) {
20271
21156
  const dependencies = {
20272
21157
  "@standardagents/spec": this.resolvePackageVersion("@standardagents/spec", rootDir)
20273
21158
  };
@@ -20289,6 +21174,7 @@ var PackingService = class {
20289
21174
  keywords: ["standardagent"],
20290
21175
  standardagent: {
20291
21176
  entryAgents: [agentName],
21177
+ ...parentHooks ? { parentHooks } : {},
20292
21178
  ...effects.length > 0 ? { effects } : {},
20293
21179
  ...threadEndpoints.length > 0 ? { threadEndpoints } : {}
20294
21180
  },
@@ -20582,6 +21468,22 @@ ${license || "See LICENSE file"}
20582
21468
  return agents;
20583
21469
  }
20584
21470
  };
21471
+ function validParentHooks(value) {
21472
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
21473
+ return Object.values(value).every((requirements) => {
21474
+ if (!Array.isArray(requirements)) return false;
21475
+ const seen = /* @__PURE__ */ new Set();
21476
+ return requirements.every((requirement) => {
21477
+ if (typeof requirement !== "object" || requirement === null) return false;
21478
+ const item = requirement;
21479
+ 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)) {
21480
+ return false;
21481
+ }
21482
+ seen.add(item.hook);
21483
+ return true;
21484
+ });
21485
+ });
21486
+ }
20585
21487
  var PackageDiscoveryService = class {
20586
21488
  config;
20587
21489
  constructor(config) {
@@ -20677,6 +21579,7 @@ var PackageDiscoveryService = class {
20677
21579
  }
20678
21580
  const effects = Array.isArray(pkgJson.standardagent.effects) && pkgJson.standardagent.effects.every((effectName) => typeof effectName === "string") ? pkgJson.standardagent.effects : void 0;
20679
21581
  const threadEndpoints = Array.isArray(pkgJson.standardagent.threadEndpoints) && pkgJson.standardagent.threadEndpoints.every((endpoint) => typeof endpoint === "string") ? pkgJson.standardagent.threadEndpoints : void 0;
21582
+ const parentHooks = validParentHooks(pkgJson.standardagent.parentHooks) ? pkgJson.standardagent.parentHooks : void 0;
20680
21583
  return {
20681
21584
  packageId: pkgJson.name || pkgName,
20682
21585
  version: pkgJson.version || "0.0.0",
@@ -20685,6 +21588,7 @@ var PackageDiscoveryService = class {
20685
21588
  entryAgents: pkgJson.standardagent.entryAgents,
20686
21589
  ...effects ? { effects } : {},
20687
21590
  ...threadEndpoints ? { threadEndpoints } : {},
21591
+ ...parentHooks ? { parentHooks } : {},
20688
21592
  description: pkgJson.description
20689
21593
  };
20690
21594
  } catch (error) {
@@ -20746,6 +21650,7 @@ var PackageDiscoveryService = class {
20746
21650
  }
20747
21651
  const effects = Array.isArray(pkgJson.standardagent.effects) && pkgJson.standardagent.effects.every((effectName) => typeof effectName === "string") ? pkgJson.standardagent.effects : void 0;
20748
21652
  const threadEndpoints = Array.isArray(pkgJson.standardagent.threadEndpoints) && pkgJson.standardagent.threadEndpoints.every((endpoint) => typeof endpoint === "string") ? pkgJson.standardagent.threadEndpoints : void 0;
21653
+ const parentHooks = validParentHooks(pkgJson.standardagent.parentHooks) ? pkgJson.standardagent.parentHooks : void 0;
20749
21654
  return {
20750
21655
  packageId: pkgJson.name || dirName,
20751
21656
  version: pkgJson.version || "0.0.0",
@@ -20754,6 +21659,7 @@ var PackageDiscoveryService = class {
20754
21659
  entryAgents: pkgJson.standardagent.entryAgents,
20755
21660
  ...effects ? { effects } : {},
20756
21661
  ...threadEndpoints ? { threadEndpoints } : {},
21662
+ ...parentHooks ? { parentHooks } : {},
20757
21663
  description: pkgJson.description
20758
21664
  };
20759
21665
  } catch (error) {
@@ -20782,7 +21688,8 @@ var PackageDiscoveryService = class {
20782
21688
  const hasValidEntryAgents = Array.isArray(f.entryAgents) && f.entryAgents.length > 0 && f.entryAgents.every((e) => typeof e === "string");
20783
21689
  const hasValidEffects = f.effects === void 0 || Array.isArray(f.effects) && f.effects.every((effectName) => typeof effectName === "string");
20784
21690
  const hasValidThreadEndpoints = f.threadEndpoints === void 0 || Array.isArray(f.threadEndpoints) && f.threadEndpoints.every((endpoint) => typeof endpoint === "string");
20785
- return hasValidEntryAgents && hasValidEffects && hasValidThreadEndpoints;
21691
+ const hasValidParentHooks = f.parentHooks === void 0 || validParentHooks(f.parentHooks);
21692
+ return hasValidEntryAgents && hasValidEffects && hasValidThreadEndpoints && hasValidParentHooks;
20786
21693
  }
20787
21694
  /**
20788
21695
  * Get a package by ID.
@@ -22410,13 +23317,13 @@ async function loadRegistryVirtualModule(context) {
22410
23317
  const absPath = toAbsolutePath(importPath);
22411
23318
  return ` "${name}": async () => (await import("${absPath}")).default,`;
22412
23319
  }).join("\n");
22413
- const packedDir = path9__default.resolve(process.cwd(), "agents/packed");
23320
+ const projectRoot = process.cwd();
22414
23321
  const packedPackages = [];
22415
- if (fs9__default.existsSync(packedDir)) {
22416
- console.log(`[vite-plugin-agent] Scanning packed directory: ${packedDir}`);
22417
- const packageDirs = discoverPackedPackageDirectories(packedDir);
22418
- console.log(`[vite-plugin-agent] Found package dirs: ${packageDirs.join(", ")}`);
22419
- for (const pkgPath of packageDirs) {
23322
+ {
23323
+ const packageDirs = discoverStandardAgentPackageDirectories(projectRoot);
23324
+ console.log(`[vite-plugin-agent] Found Standard Agent package dirs: ${packageDirs.map((pkg) => pkg.path).join(", ")}`);
23325
+ for (const discovered of packageDirs) {
23326
+ const pkgPath = discovered.path;
22420
23327
  const pkgJsonPath = path9__default.join(pkgPath, "package.json");
22421
23328
  if (fs9__default.existsSync(pkgJsonPath)) {
22422
23329
  try {
@@ -22430,6 +23337,7 @@ async function loadRegistryVirtualModule(context) {
22430
23337
  const pkg = {
22431
23338
  packageId: pkgJson.name,
22432
23339
  version: pkgJson.version,
23340
+ source: discovered.source,
22433
23341
  entryAgents: pkgJson.standardagent.entryAgents || [],
22434
23342
  items: {
22435
23343
  agents: scanPackedDistDirectory(pkgPath, outputDir, "agents"),
@@ -22448,15 +23356,13 @@ async function loadRegistryVirtualModule(context) {
22448
23356
  }
22449
23357
  }
22450
23358
  }
22451
- } else {
22452
- console.log(`[vite-plugin-agent] Packed directory does not exist: ${packedDir}`);
22453
23359
  }
22454
23360
  console.log(`[vite-plugin-agent] Total packed packages found: ${packedPackages.length}`);
22455
23361
  const packagesCode = packedPackages.map((pkg) => {
22456
23362
  const signature = JSON.stringify({
22457
23363
  packageId: pkg.packageId,
22458
23364
  version: pkg.version,
22459
- source: "local",
23365
+ source: pkg.source,
22460
23366
  packedAt: Date.now()
22461
23367
  });
22462
23368
  const genLoaders = (items) => items.map(
@@ -22555,9 +23461,9 @@ async function loadRoutesVirtualModule(context) {
22555
23461
  );`;
22556
23462
  }).join("\n");
22557
23463
  const packedThreadRoutes = [];
22558
- const packedDir = path9__default.resolve(process.cwd(), "agents/packed");
22559
- const packedPackageDirs = discoverPackedPackageDirectories(packedDir);
22560
- for (const packagePath of packedPackageDirs) {
23464
+ const packedPackageDirs = discoverStandardAgentPackageDirectories(process.cwd());
23465
+ for (const discovered of packedPackageDirs) {
23466
+ const packagePath = discovered.path;
22561
23467
  const packageJsonPath = path9__default.join(packagePath, "package.json");
22562
23468
  if (!fs9__default.existsSync(packageJsonPath)) {
22563
23469
  continue;
@@ -26410,321 +27316,8 @@ async function decryptEnvs(encrypted, encryptionKey) {
26410
27316
  return result;
26411
27317
  }
26412
27318
 
26413
- // src/packing/resolver.ts
26414
- var NamespaceResolutionError = class extends Error {
26415
- constructor(itemType, itemName, namespace, message) {
26416
- super(message);
26417
- this.itemType = itemType;
26418
- this.itemName = itemName;
26419
- this.namespace = namespace;
26420
- this.name = "NamespaceResolutionError";
26421
- }
26422
- };
26423
- function parseQualifiedName(name) {
26424
- if (name.startsWith("@")) {
26425
- const firstSlash = name.indexOf("/");
26426
- if (firstSlash === -1) {
26427
- return [null, name];
26428
- }
26429
- const secondSlash = name.indexOf("/", firstSlash + 1);
26430
- if (secondSlash === -1) {
26431
- return [null, name];
26432
- }
26433
- return [name.substring(0, secondSlash), name.substring(secondSlash + 1)];
26434
- }
26435
- const separatorIndex = name.indexOf("/");
26436
- if (separatorIndex === -1) {
26437
- return [null, name];
26438
- }
26439
- return [name.substring(0, separatorIndex), name.substring(separatorIndex + 1)];
26440
- }
26441
- function qualifyName(packageId, shortName) {
26442
- return `${packageId}/${shortName}`;
26443
- }
26444
- function getShortName(name) {
26445
- const [, shortName] = parseQualifiedName(name);
26446
- return shortName;
26447
- }
26448
- function resolveTool(registry, name, namespace) {
26449
- const [pkgId, shortName] = parseQualifiedName(name);
26450
- if (pkgId !== null) {
26451
- const pkg = registry.packages[pkgId];
26452
- if (!pkg) {
26453
- throw new NamespaceResolutionError(
26454
- "tool",
26455
- name,
26456
- namespace,
26457
- `Package "${pkgId}" not found`
26458
- );
26459
- }
26460
- throw new NamespaceResolutionError(
26461
- "tool",
26462
- name,
26463
- namespace,
26464
- `Cannot access tool "${shortName}" from package "${pkgId}". Only entry point agents are accessible cross-package.`
26465
- );
26466
- }
26467
- if (namespace.type === "packed") {
26468
- const pkg = registry.packages[namespace.packageId];
26469
- if (!pkg) {
26470
- throw new NamespaceResolutionError(
26471
- "tool",
26472
- name,
26473
- namespace,
26474
- `Package "${namespace.packageId}" not found`
26475
- );
26476
- }
26477
- const loader2 = pkg.tools[shortName];
26478
- if (!loader2) {
26479
- throw new NamespaceResolutionError(
26480
- "tool",
26481
- name,
26482
- namespace,
26483
- `Tool "${shortName}" not found in package "${namespace.packageId}"`
26484
- );
26485
- }
26486
- return loader2;
26487
- }
26488
- const loader = registry.global.tools[shortName];
26489
- if (!loader) {
26490
- throw new NamespaceResolutionError(
26491
- "tool",
26492
- name,
26493
- namespace,
26494
- `Tool "${shortName}" not found in global namespace`
26495
- );
26496
- }
26497
- return loader;
26498
- }
26499
- function resolvePrompt(registry, name, namespace) {
26500
- const [pkgId, shortName] = parseQualifiedName(name);
26501
- if (pkgId !== null) {
26502
- throw new NamespaceResolutionError(
26503
- "prompt",
26504
- name,
26505
- namespace,
26506
- `Cannot access prompt "${shortName}" from package "${pkgId}". Prompts are package-internal.`
26507
- );
26508
- }
26509
- if (namespace.type === "packed") {
26510
- const pkg = registry.packages[namespace.packageId];
26511
- if (!pkg) {
26512
- throw new NamespaceResolutionError(
26513
- "prompt",
26514
- name,
26515
- namespace,
26516
- `Package "${namespace.packageId}" not found`
26517
- );
26518
- }
26519
- const loader2 = pkg.prompts[shortName];
26520
- if (!loader2) {
26521
- throw new NamespaceResolutionError(
26522
- "prompt",
26523
- name,
26524
- namespace,
26525
- `Prompt "${shortName}" not found in package "${namespace.packageId}"`
26526
- );
26527
- }
26528
- return loader2;
26529
- }
26530
- const loader = registry.global.prompts[shortName];
26531
- if (!loader) {
26532
- throw new NamespaceResolutionError(
26533
- "prompt",
26534
- name,
26535
- namespace,
26536
- `Prompt "${shortName}" not found in global namespace`
26537
- );
26538
- }
26539
- return loader;
26540
- }
26541
- function resolveModel(registry, name, namespace) {
26542
- const [possiblePkgId, shortName] = parseQualifiedName(name);
26543
- if (possiblePkgId !== null && registry.packages[possiblePkgId]) {
26544
- throw new NamespaceResolutionError(
26545
- "model",
26546
- name,
26547
- namespace,
26548
- `Cannot use qualified model name "${name}". Models must be unqualified. If this is a provider model name, ensure no package named "${possiblePkgId}" exists.`
26549
- );
26550
- }
26551
- const lookupName = name;
26552
- if (namespace.type === "packed") {
26553
- const pkg = registry.packages[namespace.packageId];
26554
- if (pkg) {
26555
- const loader2 = pkg.models[lookupName];
26556
- if (loader2) {
26557
- return loader2;
26558
- }
26559
- }
26560
- const globalLoader = registry.global.models[lookupName];
26561
- if (globalLoader) {
26562
- return globalLoader;
26563
- }
26564
- throw new NamespaceResolutionError(
26565
- "model",
26566
- name,
26567
- namespace,
26568
- `Model "${lookupName}" not found in package "${namespace.packageId}" or global namespace`
26569
- );
26570
- }
26571
- const loader = registry.global.models[lookupName];
26572
- if (!loader) {
26573
- throw new NamespaceResolutionError(
26574
- "model",
26575
- name,
26576
- namespace,
26577
- `Model "${lookupName}" not found in global namespace`
26578
- );
26579
- }
26580
- return loader;
26581
- }
26582
- function resolveAgent(registry, name, namespace) {
26583
- const [pkgId, shortName] = parseQualifiedName(name);
26584
- if (pkgId !== null) {
26585
- const pkg = registry.packages[pkgId];
26586
- if (!pkg) {
26587
- throw new NamespaceResolutionError(
26588
- "agent",
26589
- name,
26590
- namespace,
26591
- `Package "${pkgId}" not found`
26592
- );
26593
- }
26594
- if (!pkg.entryAgents.includes(shortName)) {
26595
- throw new NamespaceResolutionError(
26596
- "agent",
26597
- name,
26598
- namespace,
26599
- `Agent "${shortName}" is not an entry point in package "${pkgId}"`
26600
- );
26601
- }
26602
- const loader = pkg.agents[shortName];
26603
- if (!loader) {
26604
- throw new NamespaceResolutionError(
26605
- "agent",
26606
- name,
26607
- namespace,
26608
- `Agent "${shortName}" not found in package "${pkgId}"`
26609
- );
26610
- }
26611
- return loader;
26612
- }
26613
- if (namespace.type === "packed") {
26614
- const pkg = registry.packages[namespace.packageId];
26615
- if (!pkg) {
26616
- throw new NamespaceResolutionError(
26617
- "agent",
26618
- name,
26619
- namespace,
26620
- `Package "${namespace.packageId}" not found`
26621
- );
26622
- }
26623
- const loader = pkg.agents[shortName];
26624
- if (!loader) {
26625
- throw new NamespaceResolutionError(
26626
- "agent",
26627
- name,
26628
- namespace,
26629
- `Agent "${shortName}" not found in package "${namespace.packageId}"`
26630
- );
26631
- }
26632
- return loader;
26633
- }
26634
- const globalLoader = registry.global.agents[shortName];
26635
- if (globalLoader) {
26636
- return globalLoader;
26637
- }
26638
- for (const [pkgId2, pkg] of Object.entries(registry.packages)) {
26639
- if (pkg.entryAgents.includes(shortName)) {
26640
- const loader = pkg.agents[shortName];
26641
- if (loader) {
26642
- return loader;
26643
- }
26644
- }
26645
- }
26646
- throw new NamespaceResolutionError(
26647
- "agent",
26648
- name,
26649
- namespace,
26650
- `Agent "${shortName}" not found in global namespace`
26651
- );
26652
- }
26653
- function resolveHook(registry, name, namespace) {
26654
- const [pkgId, shortName] = parseQualifiedName(name);
26655
- if (pkgId !== null) {
26656
- throw new NamespaceResolutionError(
26657
- "hook",
26658
- name,
26659
- namespace,
26660
- `Cannot use qualified hook name "${name}". Hooks must be unqualified.`
26661
- );
26662
- }
26663
- if (namespace.type === "packed") {
26664
- const pkg = registry.packages[namespace.packageId];
26665
- if (pkg) {
26666
- const loader2 = pkg.hooks[shortName];
26667
- if (loader2) {
26668
- return loader2;
26669
- }
26670
- }
26671
- return null;
26672
- }
26673
- const loader = registry.global.hooks[shortName];
26674
- return loader || null;
26675
- }
26676
- function getVisibleToolNames(registry, namespace) {
26677
- if (namespace.type === "packed") {
26678
- const pkg = registry.packages[namespace.packageId];
26679
- return pkg ? Object.keys(pkg.tools) : [];
26680
- }
26681
- return Object.keys(registry.global.tools);
26682
- }
26683
- function getVisibleAgentNames(registry, namespace) {
26684
- if (namespace.type === "packed") {
26685
- const pkg = registry.packages[namespace.packageId];
26686
- const ownAgents = pkg ? Object.keys(pkg.agents) : [];
26687
- const otherEntryPoints = Object.entries(registry.packages).filter(([id]) => id !== namespace.packageId).flatMap(
26688
- ([pkgId, pkg2]) => pkg2.entryAgents.map((name) => qualifyName(pkgId, name))
26689
- );
26690
- return [...ownAgents, ...otherEntryPoints];
26691
- }
26692
- const globalAgents = Object.keys(registry.global.agents);
26693
- const entryPoints = Object.values(registry.packages).flatMap((pkg) => pkg.entryAgents);
26694
- return [.../* @__PURE__ */ new Set([...globalAgents, ...entryPoints])];
26695
- }
26696
- function getVisiblePromptNames(registry, namespace) {
26697
- if (namespace.type === "packed") {
26698
- const pkg = registry.packages[namespace.packageId];
26699
- return pkg ? Object.keys(pkg.prompts) : [];
26700
- }
26701
- return Object.keys(registry.global.prompts);
26702
- }
26703
- function getVisibleModelNames(registry, namespace) {
26704
- if (namespace.type === "packed") {
26705
- const pkg = registry.packages[namespace.packageId];
26706
- const ownModels = pkg ? Object.keys(pkg.models) : [];
26707
- const globalModels = Object.keys(registry.global.models);
26708
- return [.../* @__PURE__ */ new Set([...ownModels, ...globalModels])];
26709
- }
26710
- return Object.keys(registry.global.models);
26711
- }
26712
- function createNamespaceContext(agentDef) {
26713
- if (agentDef?.__package) {
26714
- return { type: "packed", packageId: agentDef.__package.packageId };
26715
- }
26716
- return { type: "global" };
26717
- }
26718
- function isQualifiedName(name) {
26719
- if (name.startsWith("@")) {
26720
- const firstSlash = name.indexOf("/");
26721
- if (firstSlash === -1) return false;
26722
- return name.indexOf("/", firstSlash + 1) !== -1;
26723
- }
26724
- return name.includes("/");
26725
- }
26726
-
26727
27319
  // src/durable-objects/DurableAgentBuilder.ts
27320
+ init_resolver();
26728
27321
  init_types();
26729
27322
 
26730
27323
  // src/skills/frontmatter.ts
@@ -26816,6 +27409,7 @@ function validateSkillFiles(files) {
26816
27409
 
26817
27410
  // src/durable-objects/DurableAgentBuilder.ts
26818
27411
  init_scope();
27412
+ init_ParentHookResolver();
26819
27413
  var ENV_TYPES_PROPERTY = "__agentbuilder_env_types";
26820
27414
  function parseStoredPermissions(value) {
26821
27415
  if (typeof value !== "string" || !value.trim()) return null;
@@ -27497,16 +28091,29 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
27497
28091
  } catch {
27498
28092
  return;
27499
28093
  }
27500
- const hookIds = Array.isArray(agentDef?.hooks) ? agentDef.hooks : [];
27501
- if (hookIds.length === 0) {
27502
- return;
27503
- }
27504
- let hooksRegistry;
27505
- try {
27506
- hooksRegistry = this.hooks();
27507
- } catch {
27508
- return;
27509
- }
28094
+ const packageId = agentDef?.__package?.packageId;
28095
+ const rawPromptName = agentDef?.sideA?.prompt;
28096
+ const promptName = rawPromptName && packageId && !isQualifiedName(rawPromptName) ? `${packageId}/${rawPromptName}` : rawPromptName;
28097
+ const promptDef = promptName ? await this.loadPrompt(promptName) : void 0;
28098
+ const bindings = await resolveHooksForPrompt(
28099
+ {
28100
+ host: this,
28101
+ agentName: thread.agent_name,
28102
+ agent: agentDef,
28103
+ promptName,
28104
+ prompt: promptDef,
28105
+ isOptionalRelationshipEnabled: async (envName) => {
28106
+ const value = await this.resolveThreadEnv({
28107
+ threadId: thread.id,
28108
+ property: envName,
28109
+ promptName: promptName ?? agentDef.name ?? thread.agent_name
28110
+ });
28111
+ return ["true", "1", "yes"].includes((value ?? "").trim().toLowerCase());
28112
+ }
28113
+ },
28114
+ "after_thread_created"
28115
+ );
28116
+ if (bindings.length === 0) return;
27510
28117
  const durableId = this.env.AGENT_BUILDER_THREAD.idFromName(thread.id);
27511
28118
  const threadStub = this.env.AGENT_BUILDER_THREAD.get(durableId);
27512
28119
  const { ThreadStateImpl: ThreadStateImpl2 } = await Promise.resolve().then(() => (init_ThreadStateImpl(), ThreadStateImpl_exports));
@@ -27521,16 +28128,14 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
27521
28128
  terminated: thread.terminated,
27522
28129
  created_at: thread.created_at
27523
28130
  });
27524
- for (const hookId of hookIds) {
27525
- const hookLoader = hooksRegistry[hookId];
27526
- if (!hookLoader) continue;
28131
+ for (const binding of bindings) {
27527
28132
  try {
27528
- const hookDef = await hookLoader();
27529
- if (hookDef?.hook === "after_thread_created") {
27530
- await hookDef.execute(threadState);
27531
- }
28133
+ await binding.execute(threadState, binding.context);
27532
28134
  } catch (error) {
27533
- console.error(`[Hooks] \u2717 Error running after_thread_created hook '${hookId}':`, error);
28135
+ console.error(
28136
+ `[Hooks] \u2717 Error running after_thread_created hook '${binding.id}':`,
28137
+ error
28138
+ );
27534
28139
  }
27535
28140
  }
27536
28141
  }
@@ -28742,6 +29347,15 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
28742
29347
  * Load a prompt definition by name.
28743
29348
  */
28744
29349
  async loadPrompt(name) {
29350
+ if (isQualifiedName(name)) {
29351
+ const [packageId, promptName] = parseQualifiedName(name);
29352
+ const pkg = packageId ? this.registry()?.packages?.[packageId] : void 0;
29353
+ const loader2 = pkg?.prompts?.[promptName];
29354
+ if (!loader2) {
29355
+ throw new Error(`Prompt not found: ${name}`);
29356
+ }
29357
+ return await loader2();
29358
+ }
28745
29359
  const registry = this.prompts();
28746
29360
  const loader = registry[name];
28747
29361
  if (!loader) {
@@ -30030,6 +30644,8 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
30030
30644
  // src/durable-objects/DurableThread.ts
30031
30645
  init_StreamManager();
30032
30646
  init_LLMRequest();
30647
+ init_ThreadStateImpl();
30648
+ init_resolver();
30033
30649
 
30034
30650
  // src/durable-objects/logDetailStream.ts
30035
30651
  var LOG_DETAIL_HEAVY_FIELDS = [
@@ -30703,6 +31319,7 @@ var DurableThread = class _DurableThread extends DurableObject {
30703
31319
  type: agentDef.type,
30704
31320
  created_at: Date.now(),
30705
31321
  max_session_turns: agentDef.maxSessionTurns,
31322
+ _package: agentDef.__package,
30706
31323
  side_a_label: agentDef.sideA?.label,
30707
31324
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
30708
31325
  side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
@@ -30732,7 +31349,8 @@ var DurableThread = class _DurableThread extends DurableObject {
30732
31349
  side_b_session_fail_attachments_property: sideBSession.fail.attachmentsProperty,
30733
31350
  side_b_session_status_tool: sideBSession.status.toolName,
30734
31351
  side_b_session_status_message_property: sideBSession.status.messageProperty,
30735
- side_b_session_status_attachments_property: sideBSession.status.attachmentsProperty
31352
+ side_b_session_status_attachments_property: sideBSession.status.attachmentsProperty,
31353
+ hooks: Array.isArray(agentDef.hooks) ? agentDef.hooks : void 0
30736
31354
  };
30737
31355
  }
30738
31356
  /**
@@ -31402,6 +32020,29 @@ var DurableThread = class _DurableThread extends DurableObject {
31402
32020
  }
31403
32021
  return projections;
31404
32022
  }
32023
+ /**
32024
+ * Build hook resolution context and a genuine at-rest ThreadState.
32025
+ * Resolution failures abort insertion so lifecycle hooks cannot be bypassed.
32026
+ */
32027
+ async buildAtRestHookState(threadId) {
32028
+ try {
32029
+ const metadata = await this.getThreadMetadata(threadId);
32030
+ const agentConfig = await this.loadAgentConfig(metadata.agent_id);
32031
+ const threadInstance = this;
32032
+ return {
32033
+ hookState: {
32034
+ agentConfig,
32035
+ thread: { instance: threadInstance, metadata }
32036
+ },
32037
+ threadState: ThreadStateImpl.fromThreadInstance(threadInstance, metadata)
32038
+ };
32039
+ } catch (error) {
32040
+ throw new Error(
32041
+ `Unable to resolve create-message hooks for ${threadId}`,
32042
+ { cause: error }
32043
+ );
32044
+ }
32045
+ }
31405
32046
  /**
31406
32047
  * Inject a top-level message without starting a new execution.
31407
32048
  *
@@ -31414,22 +32055,7 @@ var DurableThread = class _DurableThread extends DurableObject {
31414
32055
  const id = crypto.randomUUID();
31415
32056
  const createdAt = Date.now() * TIMESTAMP_MULTIPLIER;
31416
32057
  const silent = message.silent === true;
31417
- const metadataJson = message.metadata ? JSON.stringify(message.metadata) : null;
31418
- await this.ctx.storage.sql.exec(
31419
- `INSERT INTO messages (id, role, content, created_at, status, silent, parent_id, depth, metadata, prompt)
31420
- VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)`,
31421
- id,
31422
- message.role,
31423
- message.content,
31424
- createdAt,
31425
- "completed",
31426
- silent ? 1 : 0,
31427
- null,
31428
- 0,
31429
- metadataJson,
31430
- null
31431
- );
31432
- const inserted = {
32058
+ let inserted = {
31433
32059
  id,
31434
32060
  role: message.role,
31435
32061
  content: message.content,
@@ -31440,9 +32066,37 @@ var DurableThread = class _DurableThread extends DurableObject {
31440
32066
  depth: 0,
31441
32067
  metadata: message.metadata
31442
32068
  };
32069
+ const { hookState, threadState } = await this.buildAtRestHookState(threadId);
32070
+ const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
32071
+ inserted = await FlowEngine2.runBeforeCreateMessageHook(
32072
+ hookState,
32073
+ inserted,
32074
+ threadState,
32075
+ { propagateHookErrors: true }
32076
+ );
32077
+ const metadataJson = inserted.metadata ? JSON.stringify(inserted.metadata) : null;
32078
+ await this.ctx.storage.sql.exec(
32079
+ `INSERT INTO messages (id, role, content, created_at, status, silent, parent_id, depth, metadata, prompt)
32080
+ VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)`,
32081
+ inserted.id,
32082
+ inserted.role,
32083
+ inserted.content,
32084
+ inserted.created_at,
32085
+ inserted.status ?? "completed",
32086
+ inserted.silent ? 1 : 0,
32087
+ inserted.parent_id ?? null,
32088
+ inserted.depth ?? 0,
32089
+ metadataJson,
32090
+ null
32091
+ );
32092
+ await FlowEngine2.runAfterCreateMessageHook(
32093
+ hookState,
32094
+ inserted,
32095
+ threadState
32096
+ );
31443
32097
  this.broadcastMessage(inserted);
31444
32098
  const record = inserted;
31445
- const visibleUserInjection = message.role === "user" && !silent && typeof message.content === "string" && message.content.trim().length > 0 && !isRuntimeSubagentEnvelope(message.content);
32099
+ const visibleUserInjection = inserted.role === "user" && !inserted.silent && typeof inserted.content === "string" && inserted.content.trim().length > 0 && !isRuntimeSubagentEnvelope(inserted.content);
31446
32100
  if (visibleUserInjection || isVisibleTerminalAssistantMessage(record)) {
31447
32101
  const stamp = this.recordRegistryActivity(
31448
32102
  threadId,
@@ -35900,6 +36554,8 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
35900
36554
  type: agentDef.type,
35901
36555
  created_at: Date.now(),
35902
36556
  max_session_turns: agentDef.maxSessionTurns,
36557
+ hooks: agentDef.hooks ?? [],
36558
+ _package: agentDef.__package,
35903
36559
  side_a_label: agentDef.sideA?.label,
35904
36560
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
35905
36561
  side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
@@ -36125,6 +36781,8 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
36125
36781
  type: agentDef.type,
36126
36782
  created_at: Date.now(),
36127
36783
  max_session_turns: agentDef.maxSessionTurns,
36784
+ hooks: agentDef.hooks ?? [],
36785
+ _package: agentDef.__package,
36128
36786
  side_a_label: agentDef.sideA?.label,
36129
36787
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
36130
36788
  side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
@@ -36281,6 +36939,8 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
36281
36939
  type: agentDef.type,
36282
36940
  created_at: Date.now(),
36283
36941
  max_session_turns: agentDef.maxSessionTurns,
36942
+ hooks: agentDef.hooks ?? [],
36943
+ _package: agentDef.__package,
36284
36944
  side_a_label: agentDef.sideA?.label,
36285
36945
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
36286
36946
  side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
@@ -37937,8 +38597,7 @@ var DurableUserHub = class extends DurableObject {
37937
38597
  init_utilities();
37938
38598
 
37939
38599
  // src/agents/FlowStateSdk.ts
37940
- init_types();
37941
- var FlowStateSdk = class _FlowStateSdk {
38600
+ var FlowStateSdk = class {
37942
38601
  /**
37943
38602
  * Queue a new tool call to be executed in the current flow
37944
38603
  *
@@ -38005,63 +38664,8 @@ var FlowStateSdk = class _FlowStateSdk {
38005
38664
  * ```
38006
38665
  */
38007
38666
  static async injectMessage(flow, options) {
38008
- const messageId = options.id ?? crypto.randomUUID();
38009
- const timestamp = Date.now() * TIMESTAMP_MULTIPLIER;
38010
- const silent = options.silent ?? false;
38011
- const forceTopLevel = options.forceTopLevel ?? false;
38012
- const message = {
38013
- id: messageId,
38014
- role: options.role,
38015
- content: options.content,
38016
- name: options.name ?? null,
38017
- tool_call_id: options.toolCallId ?? null,
38018
- created_at: timestamp,
38019
- status: "completed",
38020
- silent,
38021
- parent_id: forceTopLevel ? null : flow.parentMessageId ?? null,
38022
- depth: forceTopLevel ? 0 : flow.depth
38023
- };
38024
- if (options.beforeMessageId) {
38025
- const cursor = await flow.storage.sql.exec(
38026
- `SELECT created_at FROM messages WHERE id = ?`,
38027
- options.beforeMessageId
38028
- );
38029
- const rows = cursor.toArray();
38030
- const targetMessage = rows[0];
38031
- if (!targetMessage) {
38032
- throw new Error(
38033
- `Message with id ${options.beforeMessageId} not found`
38034
- );
38035
- }
38036
- message.created_at = targetMessage.created_at - 1;
38037
- }
38038
- const currentPrompt = flow.promptPath.length > 0 ? flow.promptPath[flow.promptPath.length - 1] : null;
38039
- await flow.storage.sql.exec(
38040
- `INSERT INTO messages (id, role, content, name, tool_call_id, created_at, status, silent, parent_id, depth, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
38041
- messageId,
38042
- message.role,
38043
- message.content,
38044
- message.name,
38045
- message.tool_call_id,
38046
- message.created_at,
38047
- message.status,
38048
- silent ? 1 : 0,
38049
- message.parent_id,
38050
- message.depth,
38051
- currentPrompt
38052
- );
38053
- if (!silent) {
38054
- flow.messageHistory.push(message);
38055
- } else {
38056
- flow.messageHistory = await _FlowStateSdk.reloadHistory(flow);
38057
- }
38058
- if (flow.emitMessage) {
38059
- flow.emitMessage(message);
38060
- }
38061
- if (options.forceTurn) {
38062
- _FlowStateSdk.forceTurn(flow, options.forceTurn);
38063
- }
38064
- return message;
38667
+ const { injectMessage: injectMessage2 } = await Promise.resolve().then(() => (init_utilities(), utilities_exports));
38668
+ return injectMessage2(flow, options);
38065
38669
  }
38066
38670
  /**
38067
38671
  * Get messages from the thread history
@@ -38150,9 +38754,9 @@ var FlowStateSdk = class _FlowStateSdk {
38150
38754
  /**
38151
38755
  * Reload message history from storage
38152
38756
  *
38153
- * Re-loads the complete message history from the thread's SQLite storage,
38154
- * applying any filter_messages hooks. This is useful when you need to refresh
38155
- * the message history after making changes (e.g., injecting silent messages).
38757
+ * Re-loads the complete message history from the thread's SQLite storage.
38758
+ * This is useful when you need to refresh the message history after making
38759
+ * changes (e.g., injecting messages).
38156
38760
  *
38157
38761
  * @param flow - The current FlowState
38158
38762
  * @returns Array of messages (reloaded from storage)
@@ -38294,6 +38898,9 @@ init_context();
38294
38898
  init_ProviderRegistry();
38295
38899
  init_types2();
38296
38900
 
38901
+ // src/packing/index.ts
38902
+ init_resolver();
38903
+
38297
38904
  export { CLIENT_STREAM_CHUNK_BYTES, CodeExecutionBridge, DurableAgentBuilder, DurableThread, DurableUserHub, FlowStateSdk, NamespaceResolutionError, PackageDiscoveryService, ProviderRegistry, ThreadWatchdog, agentbuilder, applyRuntimeCors, assistantMessageText, authenticate, buildImageDescription, builder, callClient, callClientStream, canAccessThread, cat, createNamespaceContext, createThreadEndpointHandler, decodeStreamChunk, defineController2 as defineController, discoverPackages, emitThreadEvent, encodeStreamChunk, endpointCallerFromAuth, enhanceFlowState, exists, find, forceTurn, generateAgentFile, generateImageDescription, generateModelFile, generatePromptFile, getFileStats, getMessages, getMessagesToSummarize, getShortName, getThumbnail, getUnsummarizedImageAttachments, getVisibleAgentNames, getVisibleModelNames, getVisiblePromptNames, getVisibleToolNames, grep, hasImageAttachments, hasJsonArrayEntries, head, injectMessage, isAdminOnlyApiRoute, isHostedInstance, isPublicApiRoute, isQualifiedName, isSameOriginBrowserRequest, isTrustedLocalDevelopmentRequest, isVisibleTerminalAssistantMessage, linkFile, messageTimestampSeconds, mkdir, move, optimizeImageContext, parseQualifiedName, qualifyName, queueTool, readFile, readdir, reloadHistory, rename, replaceImagesWithDescriptions, requireAdmin, requireAuth, resolveAgent, resolveHook, resolveModel, resolvePrompt, resolveTool, rmdir, runtimeCorsHeaders, runtimePreflightResponse, stat, supportsClientForwarding, tail, unlink, updateThread, writeFile, writeImage };
38298
38905
  //# sourceMappingURL=index.js.map
38299
38906
  //# sourceMappingURL=index.js.map