@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/runtime.js CHANGED
@@ -1489,6 +1489,9 @@ function normalizeToolCallMessageSequence(messages) {
1489
1489
  for (const message of messages) {
1490
1490
  const isAssistantToolCallMessage = message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
1491
1491
  if (!pending) {
1492
+ if (message.role === "tool") {
1493
+ continue;
1494
+ }
1492
1495
  if (!isAssistantToolCallMessage) {
1493
1496
  normalized.push(message);
1494
1497
  continue;
@@ -6797,125 +6800,667 @@ var init_context = __esm({
6797
6800
  }
6798
6801
  });
6799
6802
 
6800
- // src/agents/FlowEngine.ts
6801
- var FlowEngine_exports = {};
6802
- __export(FlowEngine_exports, {
6803
- FlowEngine: () => FlowEngine,
6804
- HookBlockedExecutionError: () => HookBlockedExecutionError
6805
- });
6806
- var IMMEDIATE_BOOTSTRAP_TOOL_NAME, HookBlockedExecutionError, FlowEngine;
6807
- var init_FlowEngine = __esm({
6808
- "src/agents/FlowEngine.ts"() {
6809
- init_types();
6810
- init_StreamManager();
6811
- init_LLMRequest();
6812
- init_ToolExecutor();
6813
- init_context();
6814
- init_sessionTools();
6815
- init_ThreadStateImpl();
6816
- IMMEDIATE_BOOTSTRAP_TOOL_NAME = "bootstrap_immediate_subagent";
6817
- HookBlockedExecutionError = class extends Error {
6818
- constructor(message) {
6819
- super(message);
6820
- this.name = "HookBlockedExecutionError";
6821
- }
6822
- };
6823
- FlowEngine = class _FlowEngine {
6824
- static IMMEDIATE_SCOPED_ENV_CONTEXT_KEY = "__immediate_scoped_env_by_call_id";
6825
- /**
6826
- * Main execution entry point
6827
- * Executes an agent conversation until stopped or max steps reached
6828
- *
6829
- * By default, returns immediately with the stream while execution continues in background.
6830
- * Set awaitCompletion: true to wait for all steps and database writes to complete (required for sub-prompts).
6831
- *
6832
- * @param stateInput - Partial FlowState object with required fields
6833
- * @param awaitCompletion - If true, awaits full execution completion before returning (default: false)
6834
- */
6835
- static async execute(stateInput, awaitCompletion = false) {
6836
- const state = await this.initializeState(stateInput);
6837
- if (awaitCompletion) {
6838
- try {
6839
- await this.executeAsync(state, state.threadId);
6840
- } catch (error) {
6841
- console.error("Fatal error in executeAsync:", error);
6842
- await this.logError(state, error, "fatal_execution_error", "FlowEngine.executeAsync");
6843
- await this.settleTerminalFailure(state, error);
6844
- throw error;
6845
- }
6846
- } else {
6847
- this.executeAsync(state, state.threadId).catch(async (error) => {
6848
- console.error("Fatal error in executeAsync:", error);
6849
- await this.logError(state, error, "fatal_execution_error", "FlowEngine.executeAsync");
6850
- await this.settleTerminalFailure(state, error);
6851
- });
6852
- }
6853
- return {
6854
- messages: state.messageHistory,
6855
- stopped: state.stopped,
6856
- stoppedBy: state.stoppedBy,
6857
- stopReason: state.stopReason,
6858
- stopReasonCode: state.stopReasonCode,
6859
- sessionStopped: state.sessionStopped,
6860
- sessionStopResult: state.sessionStopResult,
6861
- stepCount: state.stepCount,
6862
- stream: state.stream.httpStream
6863
- };
6864
- }
6865
- /**
6866
- * Execute the agentic loop asynchronously
6867
- * This runs in the background while the stream is being consumed
6868
- */
6869
- /**
6870
- * Finalize logs left incomplete by a previous, abandoned execution.
6871
- *
6872
- * A log row is created with is_complete=0 the moment a request is sent and is
6873
- * only flipped complete by logSuccess/logError. If the Durable Object is
6874
- * evicted (or crashes) while awaiting a hung provider, neither runs and the row
6875
- * is stranded incomplete forever — which downstream reads as "the agent is
6876
- * still running". A fresh top-level execution proves the prior request is no
6877
- * longer in flight, so we mark any lingering incomplete logs as failed and tell
6878
- * connected clients so their "running" state clears. Best-effort: never let
6879
- * this cleanup block or fail the execution.
6880
- */
6881
- static async finalizeOrphanedLogs(state) {
6882
- try {
6883
- const rows = (await state.storage.sql.exec(
6884
- `SELECT id FROM logs WHERE is_complete = 0`
6885
- )).toArray();
6886
- if (rows.length === 0) return;
6887
- const error = "Request did not complete \u2014 the response was interrupted (the worker was evicted or the provider stalled) before it finished.";
6888
- const errorType = "interrupted_incomplete";
6889
- await state.storage.sql.exec(
6890
- `UPDATE logs SET is_complete = 1, error = ?1, error_type = ?2 WHERE is_complete = 0`,
6891
- error,
6892
- errorType
6893
- );
6894
- for (const row of rows) {
6895
- state.emitLog?.({
6896
- type: "log_data",
6897
- log_id: row.id,
6898
- data: { id: row.id, is_complete: true, error, error_type: errorType }
6899
- });
6900
- }
6901
- console.warn(
6902
- `[FlowEngine] Finalized ${rows.length} orphaned incomplete log(s) for thread ${state.threadId}`
6903
- );
6904
- await state.thread.instance.failPendingMessagesAndBroadcast?.();
6905
- } catch (err) {
6906
- console.error("[FlowEngine] Failed to finalize orphaned logs (non-fatal):", err);
6907
- }
6803
+ // src/packing/resolver.ts
6804
+ function parseQualifiedName(name) {
6805
+ if (name.startsWith("@")) {
6806
+ const firstSlash = name.indexOf("/");
6807
+ if (firstSlash === -1) {
6808
+ return [null, name];
6809
+ }
6810
+ const secondSlash = name.indexOf("/", firstSlash + 1);
6811
+ if (secondSlash === -1) {
6812
+ return [null, name];
6813
+ }
6814
+ return [name.substring(0, secondSlash), name.substring(secondSlash + 1)];
6815
+ }
6816
+ const separatorIndex = name.indexOf("/");
6817
+ if (separatorIndex === -1) {
6818
+ return [null, name];
6819
+ }
6820
+ return [name.substring(0, separatorIndex), name.substring(separatorIndex + 1)];
6821
+ }
6822
+ function qualifyName(packageId, shortName) {
6823
+ return `${packageId}/${shortName}`;
6824
+ }
6825
+ function getShortName(name) {
6826
+ const [, shortName] = parseQualifiedName(name);
6827
+ return shortName;
6828
+ }
6829
+ function resolveTool(registry, name, namespace) {
6830
+ const [pkgId, shortName] = parseQualifiedName(name);
6831
+ if (pkgId !== null) {
6832
+ const pkg = registry.packages[pkgId];
6833
+ if (!pkg) {
6834
+ throw new NamespaceResolutionError(
6835
+ "tool",
6836
+ name,
6837
+ namespace,
6838
+ `Package "${pkgId}" not found`
6839
+ );
6840
+ }
6841
+ throw new NamespaceResolutionError(
6842
+ "tool",
6843
+ name,
6844
+ namespace,
6845
+ `Cannot access tool "${shortName}" from package "${pkgId}". Only entry point agents are accessible cross-package.`
6846
+ );
6847
+ }
6848
+ if (namespace.type === "packed") {
6849
+ const pkg = registry.packages[namespace.packageId];
6850
+ if (!pkg) {
6851
+ throw new NamespaceResolutionError(
6852
+ "tool",
6853
+ name,
6854
+ namespace,
6855
+ `Package "${namespace.packageId}" not found`
6856
+ );
6857
+ }
6858
+ const loader2 = pkg.tools[shortName];
6859
+ if (!loader2) {
6860
+ throw new NamespaceResolutionError(
6861
+ "tool",
6862
+ name,
6863
+ namespace,
6864
+ `Tool "${shortName}" not found in package "${namespace.packageId}"`
6865
+ );
6866
+ }
6867
+ return loader2;
6868
+ }
6869
+ const loader = registry.global.tools[shortName];
6870
+ if (!loader) {
6871
+ throw new NamespaceResolutionError(
6872
+ "tool",
6873
+ name,
6874
+ namespace,
6875
+ `Tool "${shortName}" not found in global namespace`
6876
+ );
6877
+ }
6878
+ return loader;
6879
+ }
6880
+ function resolvePrompt(registry, name, namespace) {
6881
+ const [pkgId, shortName] = parseQualifiedName(name);
6882
+ if (pkgId !== null) {
6883
+ throw new NamespaceResolutionError(
6884
+ "prompt",
6885
+ name,
6886
+ namespace,
6887
+ `Cannot access prompt "${shortName}" from package "${pkgId}". Prompts are package-internal.`
6888
+ );
6889
+ }
6890
+ if (namespace.type === "packed") {
6891
+ const pkg = registry.packages[namespace.packageId];
6892
+ if (!pkg) {
6893
+ throw new NamespaceResolutionError(
6894
+ "prompt",
6895
+ name,
6896
+ namespace,
6897
+ `Package "${namespace.packageId}" not found`
6898
+ );
6899
+ }
6900
+ const loader2 = pkg.prompts[shortName];
6901
+ if (!loader2) {
6902
+ throw new NamespaceResolutionError(
6903
+ "prompt",
6904
+ name,
6905
+ namespace,
6906
+ `Prompt "${shortName}" not found in package "${namespace.packageId}"`
6907
+ );
6908
+ }
6909
+ return loader2;
6910
+ }
6911
+ const loader = registry.global.prompts[shortName];
6912
+ if (!loader) {
6913
+ throw new NamespaceResolutionError(
6914
+ "prompt",
6915
+ name,
6916
+ namespace,
6917
+ `Prompt "${shortName}" not found in global namespace`
6918
+ );
6919
+ }
6920
+ return loader;
6921
+ }
6922
+ function resolveModel(registry, name, namespace) {
6923
+ const [possiblePkgId, shortName] = parseQualifiedName(name);
6924
+ if (possiblePkgId !== null && registry.packages[possiblePkgId]) {
6925
+ throw new NamespaceResolutionError(
6926
+ "model",
6927
+ name,
6928
+ namespace,
6929
+ `Cannot use qualified model name "${name}". Models must be unqualified. If this is a provider model name, ensure no package named "${possiblePkgId}" exists.`
6930
+ );
6931
+ }
6932
+ const lookupName = name;
6933
+ if (namespace.type === "packed") {
6934
+ const pkg = registry.packages[namespace.packageId];
6935
+ if (pkg) {
6936
+ const loader2 = pkg.models[lookupName];
6937
+ if (loader2) {
6938
+ return loader2;
6908
6939
  }
6909
- /**
6910
- * A terminal execution failure ended the run without a normal assistant
6911
- * reply. Persist a durable failed assistant message + settle pending
6912
- * messages so every connected head lands on a visible end state instead of
6913
- * a stuck "Working" spinner. Best-effort — never masks the original error.
6914
- */
6915
- static async settleTerminalFailure(state, error) {
6916
- try {
6917
- const detail = error instanceof Error ? error.message : String(error);
6918
- await state.thread.instance.settleTerminalFailure?.(
6940
+ }
6941
+ const globalLoader = registry.global.models[lookupName];
6942
+ if (globalLoader) {
6943
+ return globalLoader;
6944
+ }
6945
+ throw new NamespaceResolutionError(
6946
+ "model",
6947
+ name,
6948
+ namespace,
6949
+ `Model "${lookupName}" not found in package "${namespace.packageId}" or global namespace`
6950
+ );
6951
+ }
6952
+ const loader = registry.global.models[lookupName];
6953
+ if (!loader) {
6954
+ throw new NamespaceResolutionError(
6955
+ "model",
6956
+ name,
6957
+ namespace,
6958
+ `Model "${lookupName}" not found in global namespace`
6959
+ );
6960
+ }
6961
+ return loader;
6962
+ }
6963
+ function resolveAgent(registry, name, namespace) {
6964
+ const [pkgId, shortName] = parseQualifiedName(name);
6965
+ if (pkgId !== null) {
6966
+ const pkg = registry.packages[pkgId];
6967
+ if (!pkg) {
6968
+ throw new NamespaceResolutionError(
6969
+ "agent",
6970
+ name,
6971
+ namespace,
6972
+ `Package "${pkgId}" not found`
6973
+ );
6974
+ }
6975
+ if (!pkg.entryAgents.includes(shortName)) {
6976
+ throw new NamespaceResolutionError(
6977
+ "agent",
6978
+ name,
6979
+ namespace,
6980
+ `Agent "${shortName}" is not an entry point in package "${pkgId}"`
6981
+ );
6982
+ }
6983
+ const loader = pkg.agents[shortName];
6984
+ if (!loader) {
6985
+ throw new NamespaceResolutionError(
6986
+ "agent",
6987
+ name,
6988
+ namespace,
6989
+ `Agent "${shortName}" not found in package "${pkgId}"`
6990
+ );
6991
+ }
6992
+ return loader;
6993
+ }
6994
+ if (namespace.type === "packed") {
6995
+ const pkg = registry.packages[namespace.packageId];
6996
+ if (!pkg) {
6997
+ throw new NamespaceResolutionError(
6998
+ "agent",
6999
+ name,
7000
+ namespace,
7001
+ `Package "${namespace.packageId}" not found`
7002
+ );
7003
+ }
7004
+ const loader = pkg.agents[shortName];
7005
+ if (!loader) {
7006
+ throw new NamespaceResolutionError(
7007
+ "agent",
7008
+ name,
7009
+ namespace,
7010
+ `Agent "${shortName}" not found in package "${namespace.packageId}"`
7011
+ );
7012
+ }
7013
+ return loader;
7014
+ }
7015
+ const globalLoader = registry.global.agents[shortName];
7016
+ if (globalLoader) {
7017
+ return globalLoader;
7018
+ }
7019
+ for (const [pkgId2, pkg] of Object.entries(registry.packages)) {
7020
+ if (pkg.entryAgents.includes(shortName)) {
7021
+ const loader = pkg.agents[shortName];
7022
+ if (loader) {
7023
+ return loader;
7024
+ }
7025
+ }
7026
+ }
7027
+ throw new NamespaceResolutionError(
7028
+ "agent",
7029
+ name,
7030
+ namespace,
7031
+ `Agent "${shortName}" not found in global namespace`
7032
+ );
7033
+ }
7034
+ function resolveHook(registry, name, namespace) {
7035
+ const [pkgId, shortName] = parseQualifiedName(name);
7036
+ if (pkgId !== null) {
7037
+ throw new NamespaceResolutionError(
7038
+ "hook",
7039
+ name,
7040
+ namespace,
7041
+ `Cannot use qualified hook name "${name}". Hooks must be unqualified.`
7042
+ );
7043
+ }
7044
+ if (namespace.type === "packed") {
7045
+ const pkg = registry.packages[namespace.packageId];
7046
+ if (pkg) {
7047
+ const loader2 = pkg.hooks[shortName];
7048
+ if (loader2) {
7049
+ return loader2;
7050
+ }
7051
+ }
7052
+ return null;
7053
+ }
7054
+ const loader = registry.global.hooks[shortName];
7055
+ return loader || null;
7056
+ }
7057
+ function getVisibleToolNames(registry, namespace) {
7058
+ if (namespace.type === "packed") {
7059
+ const pkg = registry.packages[namespace.packageId];
7060
+ return pkg ? Object.keys(pkg.tools) : [];
7061
+ }
7062
+ return Object.keys(registry.global.tools);
7063
+ }
7064
+ function getVisibleAgentNames(registry, namespace) {
7065
+ if (namespace.type === "packed") {
7066
+ const pkg = registry.packages[namespace.packageId];
7067
+ const ownAgents = pkg ? Object.keys(pkg.agents) : [];
7068
+ const otherEntryPoints = Object.entries(registry.packages).filter(([id]) => id !== namespace.packageId).flatMap(
7069
+ ([pkgId, pkg2]) => pkg2.entryAgents.map((name) => qualifyName(pkgId, name))
7070
+ );
7071
+ return [...ownAgents, ...otherEntryPoints];
7072
+ }
7073
+ const globalAgents = Object.keys(registry.global.agents);
7074
+ const entryPoints = Object.values(registry.packages).flatMap((pkg) => pkg.entryAgents);
7075
+ return [.../* @__PURE__ */ new Set([...globalAgents, ...entryPoints])];
7076
+ }
7077
+ function getVisiblePromptNames(registry, namespace) {
7078
+ if (namespace.type === "packed") {
7079
+ const pkg = registry.packages[namespace.packageId];
7080
+ return pkg ? Object.keys(pkg.prompts) : [];
7081
+ }
7082
+ return Object.keys(registry.global.prompts);
7083
+ }
7084
+ function getVisibleModelNames(registry, namespace) {
7085
+ if (namespace.type === "packed") {
7086
+ const pkg = registry.packages[namespace.packageId];
7087
+ const ownModels = pkg ? Object.keys(pkg.models) : [];
7088
+ const globalModels = Object.keys(registry.global.models);
7089
+ return [.../* @__PURE__ */ new Set([...ownModels, ...globalModels])];
7090
+ }
7091
+ return Object.keys(registry.global.models);
7092
+ }
7093
+ function createNamespaceContext(agentDef) {
7094
+ if (agentDef?.__package) {
7095
+ return { type: "packed", packageId: agentDef.__package.packageId };
7096
+ }
7097
+ return { type: "global" };
7098
+ }
7099
+ function isQualifiedName(name) {
7100
+ if (name.startsWith("@")) {
7101
+ const firstSlash = name.indexOf("/");
7102
+ if (firstSlash === -1) return false;
7103
+ return name.indexOf("/", firstSlash + 1) !== -1;
7104
+ }
7105
+ return name.includes("/");
7106
+ }
7107
+ var NamespaceResolutionError;
7108
+ var init_resolver = __esm({
7109
+ "src/packing/resolver.ts"() {
7110
+ NamespaceResolutionError = class extends Error {
7111
+ constructor(itemType, itemName, namespace, message) {
7112
+ super(message);
7113
+ this.itemType = itemType;
7114
+ this.itemName = itemName;
7115
+ this.namespace = namespace;
7116
+ this.name = "NamespaceResolutionError";
7117
+ }
7118
+ };
7119
+ }
7120
+ });
7121
+
7122
+ // src/agents/ParentHookResolver.ts
7123
+ function packageOf(definition) {
7124
+ return definition?.__package ?? definition?._package;
7125
+ }
7126
+ function qualifyWithinPackage(name, packageId) {
7127
+ if (!packageId || parseQualifiedName(name)[0] !== null) {
7128
+ return name;
7129
+ }
7130
+ return qualifyName(packageId, name);
7131
+ }
7132
+ function promptHooks(prompt) {
7133
+ if (!prompt) return [];
7134
+ return Array.isArray(prompt.hooks) ? prompt.hooks : Array.isArray(prompt._hooks) ? prompt._hooks : [];
7135
+ }
7136
+ function promptTools(prompt) {
7137
+ if (!prompt) return [];
7138
+ return Array.isArray(prompt.tools) ? prompt.tools : Array.isArray(prompt._tools) ? prompt._tools : [];
7139
+ }
7140
+ function relationDescriptor(entry) {
7141
+ if (typeof entry === "string") {
7142
+ return {
7143
+ relationshipName: entry,
7144
+ implementingAgent: entry,
7145
+ explicitlyAgentShaped: false
7146
+ };
7147
+ }
7148
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
7149
+ return null;
7150
+ }
7151
+ const config = entry;
7152
+ if (typeof config.name !== "string" || !config.name) {
7153
+ return null;
7154
+ }
7155
+ const implementingAgent = typeof config.agent === "string" && config.agent ? config.agent : config.name;
7156
+ const explicitlyAgentShaped = "agent" in config || "blocking" in config || "resumable" in config || "immediate" in config || "parentCommunication" in config || "hidden" in config;
7157
+ return {
7158
+ relationshipName: config.name,
7159
+ implementingAgent,
7160
+ optional: typeof config.optional === "string" ? config.optional : void 0,
7161
+ explicitlyAgentShaped
7162
+ };
7163
+ }
7164
+ function hookLoader(host, owner, hookId) {
7165
+ const ownerPackage = packageOf(owner);
7166
+ if (!ownerPackage) {
7167
+ return host.hooks()[hookId];
7168
+ }
7169
+ return host.registry?.()?.packages?.[ownerPackage.packageId]?.hooks?.[hookId];
7170
+ }
7171
+ async function loadBinding(host, owner, hookId, hookType, context, required) {
7172
+ const loader = hookLoader(host, owner, hookId);
7173
+ if (!loader) {
7174
+ if (required) {
7175
+ throw new Error(
7176
+ `Required parent hook '${hookId}' was not found in the child agent namespace`
7177
+ );
7178
+ }
7179
+ return null;
7180
+ }
7181
+ let loaded;
7182
+ try {
7183
+ loaded = await loader();
7184
+ } catch (error) {
7185
+ if (required) throw error;
7186
+ console.error(`[Hooks] Failed to load hook '${hookId}':`, error);
7187
+ return null;
7188
+ }
7189
+ if (!loaded) {
7190
+ if (required) {
7191
+ throw new Error(`Required parent hook '${hookId}' did not export a hook definition`);
7192
+ }
7193
+ return null;
7194
+ }
7195
+ if (typeof loaded.id !== "string" || loaded.id !== hookId) {
7196
+ if (required) {
7197
+ throw new Error(`Required parent hook '${hookId}' exported a mismatched hook ID`);
7198
+ }
7199
+ return null;
7200
+ }
7201
+ if (typeof loaded.execute !== "function") {
7202
+ if (required) {
7203
+ throw new Error(`Required parent hook '${hookId}' did not export an execute function`);
7204
+ }
7205
+ return null;
7206
+ }
7207
+ if (USER_HOOK_NAMES.has(loaded.hook)) {
7208
+ if (required) {
7209
+ throw new Error(
7210
+ `Required parent hook '${hookId}' uses user-scoped hook type '${loaded.hook}'`
7211
+ );
7212
+ }
7213
+ return null;
7214
+ }
7215
+ if (!THREAD_HOOK_NAMES.has(loaded.hook)) {
7216
+ if (required) {
7217
+ throw new Error(`Required parent hook '${hookId}' exported invalid hook type '${loaded.hook}'`);
7218
+ }
7219
+ return null;
7220
+ }
7221
+ if (loaded.hook !== hookType) {
7222
+ return null;
7223
+ }
7224
+ return {
7225
+ id: hookId,
7226
+ hook: hookType,
7227
+ execute: loaded.execute,
7228
+ context
7229
+ };
7230
+ }
7231
+ async function resolveHooksForPrompt(input, hookType) {
7232
+ const agent = input.agent ?? await input.host.loadAgent(input.agentName);
7233
+ const agentPackage = packageOf(agent);
7234
+ const unqualifiedPromptName = input.promptName ?? agent.sideA?.prompt;
7235
+ const resolvedPromptName = unqualifiedPromptName ? qualifyWithinPackage(unqualifiedPromptName, agentPackage?.packageId) : void 0;
7236
+ const prompt = input.prompt ?? (resolvedPromptName ? await input.host.loadPrompt(resolvedPromptName) : void 0);
7237
+ const promptName = prompt?.name ?? prompt?.id ?? resolvedPromptName ?? "unknown_prompt";
7238
+ const before = [];
7239
+ const after = [];
7240
+ for (const entry of promptTools(prompt)) {
7241
+ const relation = relationDescriptor(entry);
7242
+ if (!relation) continue;
7243
+ if (relation.optional && input.isOptionalRelationshipEnabled && !await input.isOptionalRelationshipEnabled(relation.optional)) {
7244
+ continue;
7245
+ }
7246
+ const promptPackage = packageOf(prompt) ?? agentPackage;
7247
+ const childReference = qualifyWithinPackage(
7248
+ relation.implementingAgent,
7249
+ promptPackage?.packageId
7250
+ );
7251
+ let child;
7252
+ try {
7253
+ child = await input.host.loadAgent(childReference);
7254
+ } catch (error) {
7255
+ if (relation.explicitlyAgentShaped) throw error;
7256
+ continue;
7257
+ }
7258
+ if (child.type !== "dual_ai") continue;
7259
+ if (child.parentHooks !== void 0 && child.exposeAsTool !== true) {
7260
+ throw new Error(
7261
+ `Child agent '${childReference}' declares parentHooks but is not exposed as a tool`
7262
+ );
7263
+ }
7264
+ if (child.parentHooks !== void 0 && !Array.isArray(child.parentHooks)) {
7265
+ throw new Error(`Child agent '${childReference}' has an invalid parentHooks declaration`);
7266
+ }
7267
+ const requirements = child.parentHooks ?? [];
7268
+ const seenRequirements = /* @__PURE__ */ new Set();
7269
+ for (const requirement of requirements) {
7270
+ 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)) {
7271
+ throw new Error(`Child agent '${childReference}' has an invalid parentHooks requirement`);
7272
+ }
7273
+ seenRequirements.add(requirement.hook);
7274
+ const loaded = await loadBinding(
7275
+ input.host,
7276
+ child,
7277
+ requirement.hook,
7278
+ hookType,
7279
+ {
7280
+ source: {
7281
+ kind: "child",
7282
+ relationshipName: relation.relationshipName,
7283
+ agentName: childReference,
7284
+ package: packageOf(child)
7285
+ }
7286
+ },
7287
+ true
7288
+ );
7289
+ if (!loaded) continue;
7290
+ if ((requirement.stage ?? "after_parent") === "before_parent") {
7291
+ before.push(loaded);
7292
+ } else {
7293
+ after.push(loaded);
7294
+ }
7295
+ }
7296
+ }
7297
+ const promptHookIds = promptHooks(prompt);
7298
+ const promptOwnsBaseHooks = promptHookIds.length > 0 && prompt !== void 0;
7299
+ const baseOwner = promptOwnsBaseHooks ? prompt : agent;
7300
+ const baseIds = promptHookIds.length > 0 ? promptHookIds : Array.isArray(agent.hooks) ? agent.hooks : [];
7301
+ const baseContext = promptOwnsBaseHooks ? { source: { kind: "prompt", promptName } } : { source: { kind: "agent", agentName: input.agentName } };
7302
+ const base = [];
7303
+ for (const hookId of baseIds) {
7304
+ const loaded = await loadBinding(
7305
+ input.host,
7306
+ baseOwner,
7307
+ hookId,
7308
+ hookType,
7309
+ baseContext,
7310
+ false
7311
+ );
7312
+ if (loaded) base.push(loaded);
7313
+ }
7314
+ return [...before, ...base, ...after];
7315
+ }
7316
+ var USER_HOOK_NAMES, THREAD_HOOK_NAMES;
7317
+ var init_ParentHookResolver = __esm({
7318
+ "src/agents/ParentHookResolver.ts"() {
7319
+ init_resolver();
7320
+ USER_HOOK_NAMES = /* @__PURE__ */ new Set([
7321
+ "user_client_attached",
7322
+ "user_client_detached",
7323
+ "user_value_changed"
7324
+ ]);
7325
+ THREAD_HOOK_NAMES = /* @__PURE__ */ new Set([
7326
+ "after_thread_created",
7327
+ "after_subagent_created",
7328
+ "after_system_message",
7329
+ "filter_messages",
7330
+ "prefilter_llm_history",
7331
+ "before_create_message",
7332
+ "after_create_message",
7333
+ "before_update_message",
7334
+ "after_update_message",
7335
+ "before_store_tool_result",
7336
+ "after_tool_call_success",
7337
+ "after_tool_call_failure",
7338
+ "tool_call_started",
7339
+ "tool_call_done"
7340
+ ]);
7341
+ }
7342
+ });
7343
+
7344
+ // src/agents/FlowEngine.ts
7345
+ var FlowEngine_exports = {};
7346
+ __export(FlowEngine_exports, {
7347
+ FlowEngine: () => FlowEngine,
7348
+ HookBlockedExecutionError: () => HookBlockedExecutionError
7349
+ });
7350
+ var IMMEDIATE_BOOTSTRAP_TOOL_NAME, HookBlockedExecutionError, FlowEngine;
7351
+ var init_FlowEngine = __esm({
7352
+ "src/agents/FlowEngine.ts"() {
7353
+ init_types();
7354
+ init_StreamManager();
7355
+ init_LLMRequest();
7356
+ init_ToolExecutor();
7357
+ init_context();
7358
+ init_sessionTools();
7359
+ init_ThreadStateImpl();
7360
+ init_ParentHookResolver();
7361
+ IMMEDIATE_BOOTSTRAP_TOOL_NAME = "bootstrap_immediate_subagent";
7362
+ HookBlockedExecutionError = class extends Error {
7363
+ constructor(message) {
7364
+ super(message);
7365
+ this.name = "HookBlockedExecutionError";
7366
+ }
7367
+ };
7368
+ FlowEngine = class _FlowEngine {
7369
+ static IMMEDIATE_SCOPED_ENV_CONTEXT_KEY = "__immediate_scoped_env_by_call_id";
7370
+ /**
7371
+ * Main execution entry point
7372
+ * Executes an agent conversation until stopped or max steps reached
7373
+ *
7374
+ * By default, returns immediately with the stream while execution continues in background.
7375
+ * Set awaitCompletion: true to wait for all steps and database writes to complete (required for sub-prompts).
7376
+ *
7377
+ * @param stateInput - Partial FlowState object with required fields
7378
+ * @param awaitCompletion - If true, awaits full execution completion before returning (default: false)
7379
+ */
7380
+ static async execute(stateInput, awaitCompletion = false) {
7381
+ const state = await this.initializeState(stateInput);
7382
+ if (awaitCompletion) {
7383
+ try {
7384
+ await this.executeAsync(state, state.threadId);
7385
+ } catch (error) {
7386
+ console.error("Fatal error in executeAsync:", error);
7387
+ await this.logError(state, error, "fatal_execution_error", "FlowEngine.executeAsync");
7388
+ await this.settleTerminalFailure(state, error);
7389
+ throw error;
7390
+ }
7391
+ } else {
7392
+ this.executeAsync(state, state.threadId).catch(async (error) => {
7393
+ console.error("Fatal error in executeAsync:", error);
7394
+ await this.logError(state, error, "fatal_execution_error", "FlowEngine.executeAsync");
7395
+ await this.settleTerminalFailure(state, error);
7396
+ });
7397
+ }
7398
+ return {
7399
+ messages: state.messageHistory,
7400
+ stopped: state.stopped,
7401
+ stoppedBy: state.stoppedBy,
7402
+ stopReason: state.stopReason,
7403
+ stopReasonCode: state.stopReasonCode,
7404
+ sessionStopped: state.sessionStopped,
7405
+ sessionStopResult: state.sessionStopResult,
7406
+ stepCount: state.stepCount,
7407
+ stream: state.stream.httpStream
7408
+ };
7409
+ }
7410
+ /**
7411
+ * Execute the agentic loop asynchronously
7412
+ * This runs in the background while the stream is being consumed
7413
+ */
7414
+ /**
7415
+ * Finalize logs left incomplete by a previous, abandoned execution.
7416
+ *
7417
+ * A log row is created with is_complete=0 the moment a request is sent and is
7418
+ * only flipped complete by logSuccess/logError. If the Durable Object is
7419
+ * evicted (or crashes) while awaiting a hung provider, neither runs and the row
7420
+ * is stranded incomplete forever — which downstream reads as "the agent is
7421
+ * still running". A fresh top-level execution proves the prior request is no
7422
+ * longer in flight, so we mark any lingering incomplete logs as failed and tell
7423
+ * connected clients so their "running" state clears. Best-effort: never let
7424
+ * this cleanup block or fail the execution.
7425
+ */
7426
+ static async finalizeOrphanedLogs(state) {
7427
+ try {
7428
+ const rows = (await state.storage.sql.exec(
7429
+ `SELECT id FROM logs WHERE is_complete = 0`
7430
+ )).toArray();
7431
+ if (rows.length === 0) return;
7432
+ const error = "Request did not complete \u2014 the response was interrupted (the worker was evicted or the provider stalled) before it finished.";
7433
+ const errorType = "interrupted_incomplete";
7434
+ await state.storage.sql.exec(
7435
+ `UPDATE logs SET is_complete = 1, error = ?1, error_type = ?2 WHERE is_complete = 0`,
7436
+ error,
7437
+ errorType
7438
+ );
7439
+ for (const row of rows) {
7440
+ state.emitLog?.({
7441
+ type: "log_data",
7442
+ log_id: row.id,
7443
+ data: { id: row.id, is_complete: true, error, error_type: errorType }
7444
+ });
7445
+ }
7446
+ console.warn(
7447
+ `[FlowEngine] Finalized ${rows.length} orphaned incomplete log(s) for thread ${state.threadId}`
7448
+ );
7449
+ await state.thread.instance.failPendingMessagesAndBroadcast?.();
7450
+ } catch (err) {
7451
+ console.error("[FlowEngine] Failed to finalize orphaned logs (non-fatal):", err);
7452
+ }
7453
+ }
7454
+ /**
7455
+ * A terminal execution failure ended the run without a normal assistant
7456
+ * reply. Persist a durable failed assistant message + settle pending
7457
+ * messages so every connected head lands on a visible end state instead of
7458
+ * a stuck "Working" spinner. Best-effort — never masks the original error.
7459
+ */
7460
+ static async settleTerminalFailure(state, error) {
7461
+ try {
7462
+ const detail = error instanceof Error ? error.message : String(error);
7463
+ await state.thread.instance.settleTerminalFailure?.(
6919
7464
  `The agent run failed unexpectedly: ${detail}`,
6920
7465
  state.threadId
6921
7466
  );
@@ -7155,7 +7700,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
7155
7700
  _variables: def.variables ?? [],
7156
7701
  _env: def.env ?? def.tenvs ?? {},
7157
7702
  _requiredSchema: def.requiredSchema ?? null,
7158
- _hooks: def.hooks ?? []
7703
+ _hooks: def.hooks ?? [],
7704
+ _package: def.__package
7159
7705
  };
7160
7706
  }
7161
7707
  /**
@@ -7545,6 +8091,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
7545
8091
  type: newAgentDef.type,
7546
8092
  created_at: Date.now(),
7547
8093
  max_session_turns: newAgentDef.maxSessionTurns ?? null,
8094
+ hooks: newAgentDef.hooks ?? [],
8095
+ _package: newAgentDef.__package,
7548
8096
  side_a_label: newAgentDef.sideA?.label ?? null,
7549
8097
  side_a_agent_prompt: newAgentDef.sideA?.prompt ?? null,
7550
8098
  side_a_stop_on_response: newAgentDef.sideA?.stopOnResponse ?? true,
@@ -7635,28 +8183,12 @@ This usually points to something I can't fix from here \u2014 for example the wo
7635
8183
  state.promptPath = [sideAPrompt.name];
7636
8184
  state.pendingImmediateAfterHandoff = true;
7637
8185
  if (userMessageContent || userMessageAttachments) {
7638
- const handoffMessageId = crypto.randomUUID();
7639
- const handoffTimestamp = Date.now() * 1e3;
7640
- const handoffMessage = {
7641
- id: handoffMessageId,
7642
- role: "user",
7643
- content: userMessageContent || "",
7644
- created_at: handoffTimestamp,
7645
- attachments: userMessageAttachments
7646
- };
7647
- await state.storage.sql.exec(
7648
- `INSERT INTO messages (id, role, content, attachments, created_at, prompt) VALUES (?, ?, ?, ?, ?, ?)`,
7649
- handoffMessage.id,
7650
- handoffMessage.role,
7651
- handoffMessage.content,
7652
- handoffMessage.attachments || null,
7653
- handoffMessage.created_at,
8186
+ await this.persistHandoffUserMessage(
8187
+ state,
8188
+ userMessageContent || "",
8189
+ userMessageAttachments,
7654
8190
  sideAPrompt.name
7655
8191
  );
7656
- state.messageHistory.push(handoffMessage);
7657
- if (state.emitMessage) {
7658
- state.emitMessage(handoffMessage);
7659
- }
7660
8192
  }
7661
8193
  state.pendingHandoff = void 0;
7662
8194
  state.emitTelemetry?.({
@@ -7671,6 +8203,37 @@ This usually points to something I can't fix from here \u2014 for example the wo
7671
8203
  throw error;
7672
8204
  }
7673
8205
  }
8206
+ /**
8207
+ * Persist the user message that rides an agent handoff, applying the
8208
+ * create-message lifecycle hooks like every other insertion path.
8209
+ */
8210
+ static async persistHandoffUserMessage(state, content, attachments, promptName) {
8211
+ let handoffMessage = {
8212
+ id: crypto.randomUUID(),
8213
+ role: "user",
8214
+ content,
8215
+ created_at: Date.now() * 1e3,
8216
+ status: "completed",
8217
+ attachments: attachments ?? null
8218
+ };
8219
+ handoffMessage = await this.runBeforeCreateMessageHook(state, handoffMessage);
8220
+ await state.storage.sql.exec(
8221
+ `INSERT INTO messages (id, role, content, attachments, created_at, status, silent, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
8222
+ handoffMessage.id,
8223
+ handoffMessage.role,
8224
+ handoffMessage.content,
8225
+ handoffMessage.attachments || null,
8226
+ handoffMessage.created_at,
8227
+ handoffMessage.status ?? "completed",
8228
+ handoffMessage.silent ? 1 : 0,
8229
+ promptName
8230
+ );
8231
+ state.messageHistory.push(handoffMessage);
8232
+ await this.runAfterCreateMessageHook(state, handoffMessage);
8233
+ if (state.emitMessage) {
8234
+ state.emitMessage(handoffMessage);
8235
+ }
8236
+ }
7674
8237
  static isEnabledEnvFlag(value) {
7675
8238
  if (value === null || value === void 0) return false;
7676
8239
  const normalized = value.trim().toLowerCase();
@@ -7690,7 +8253,7 @@ This usually points to something I can't fix from here \u2014 for example the wo
7690
8253
  return null;
7691
8254
  }
7692
8255
  return await stub.resolveThreadEnv({
7693
- threadId: state.threadId,
8256
+ threadId: state.thread.metadata.id,
7694
8257
  property,
7695
8258
  promptName: state.prompt?.name
7696
8259
  });
@@ -8212,8 +8775,8 @@ This usually points to something I can't fix from here \u2014 for example the wo
8212
8775
  state.context[key] = existing;
8213
8776
  }
8214
8777
  static async runImmediateToolsIfNeeded(state) {
8215
- const promptTools = Array.isArray(state.prompt._tools) ? state.prompt._tools : [];
8216
- if (promptTools.length === 0) {
8778
+ const promptTools2 = Array.isArray(state.prompt._tools) ? state.prompt._tools : [];
8779
+ if (promptTools2.length === 0) {
8217
8780
  return false;
8218
8781
  }
8219
8782
  if (!state.immediatePromptRuns) {
@@ -8249,7 +8812,7 @@ This usually points to something I can't fix from here \u2014 for example the wo
8249
8812
  (activeInstanceCounts.get(child.name) ?? 0) + 1
8250
8813
  );
8251
8814
  }
8252
- for (const toolConfig of promptTools) {
8815
+ for (const toolConfig of promptTools2) {
8253
8816
  if (!toolConfig || typeof toolConfig !== "object") {
8254
8817
  continue;
8255
8818
  }
@@ -8415,10 +8978,10 @@ This usually points to something I can't fix from here \u2014 for example the wo
8415
8978
  * model's to clean up, and terminate_subagent on an infrastructure child
8416
8979
  * kills work the runtime depends on. Static and pure for direct testing.
8417
8980
  */
8418
- static filterModelVisibleSubagents(registry, promptTools) {
8981
+ static filterModelVisibleSubagents(registry, promptTools2) {
8419
8982
  if (registry.length === 0) return registry;
8420
8983
  const hidden = /* @__PURE__ */ new Set();
8421
- for (const config of promptTools ?? []) {
8984
+ for (const config of promptTools2 ?? []) {
8422
8985
  if (typeof config === "object" && config !== null && "name" in config && config.hidden === true) {
8423
8986
  hidden.add(config.name);
8424
8987
  }
@@ -8568,7 +9131,16 @@ This usually points to something I can't fix from here \u2014 for example the wo
8568
9131
  role: "system",
8569
9132
  content: systemPromptText
8570
9133
  });
8571
- const completedMessages = state.messageHistory.filter(
9134
+ const historySnapshot = state.messageHistory.filter(
9135
+ (msg) => msg.status !== "pending"
9136
+ );
9137
+ const filteredHistory = await this.runFilterMessagesHook(
9138
+ state.thread.instance,
9139
+ state,
9140
+ historySnapshot
9141
+ );
9142
+ const contextHistory = this.filterOrphanedToolCalls(filteredHistory);
9143
+ const completedMessages = contextHistory.filter(
8572
9144
  (msg) => msg.status !== "pending"
8573
9145
  );
8574
9146
  const recentMessageThreshold = state.prompt.recentImageThreshold ?? 10;
@@ -9439,8 +10011,8 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9439
10011
  try {
9440
10012
  const nativeTools = await this.loadNativeTools(state);
9441
10013
  tools.push(...nativeTools);
9442
- const promptTools = await this.loadPromptTools(state);
9443
- tools.push(...promptTools);
10014
+ const promptTools2 = await this.loadPromptTools(state);
10015
+ tools.push(...promptTools2);
9444
10016
  const agentTools = await this.loadAgentTools(state);
9445
10017
  tools.push(...agentTools);
9446
10018
  const toolNameSources = /* @__PURE__ */ new Map();
@@ -9449,7 +10021,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9449
10021
  sources.add("native tool");
9450
10022
  toolNameSources.set(tool.function.name, sources);
9451
10023
  }
9452
- for (const tool of promptTools) {
10024
+ for (const tool of promptTools2) {
9453
10025
  const sources = toolNameSources.get(tool.function.name) || /* @__PURE__ */ new Set();
9454
10026
  sources.add("prompt");
9455
10027
  toolNameSources.set(tool.function.name, sources);
@@ -9591,24 +10163,8 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9591
10163
  // Hook Resolution Helpers
9592
10164
  // ==========================================================================
9593
10165
  /**
9594
- * Get the list of hook IDs that should run for the current context.
9595
- * Resolution: prompt hooks > agent hooks > empty array
9596
- *
9597
- * @param state - Flow state with prompt and agent configuration
9598
- * @returns Array of hook IDs to execute
9599
- */
9600
- static getActiveHookIds(state) {
9601
- if (state.prompt?._hooks !== void 0 && state.prompt._hooks.length > 0) {
9602
- return state.prompt._hooks;
9603
- }
9604
- if (state.agentConfig.hooks !== void 0 && state.agentConfig.hooks.length > 0) {
9605
- return state.agentConfig.hooks;
9606
- }
9607
- return [];
9608
- }
9609
- /**
9610
- * Get hooks of a specific type from the active hook IDs.
9611
- * Returns typed hook executors in the order they appear in the hooks array.
10166
+ * Resolve hooks of a specific type for the active prompt, including required
10167
+ * hooks contributed automatically by its enabled direct child agents.
9612
10168
  *
9613
10169
  * @template K - The hook type name (ensures type-safe executors)
9614
10170
  * @param state - Flow state with hook configuration
@@ -9616,24 +10172,17 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9616
10172
  * @returns Array of typed hook executor functions
9617
10173
  */
9618
10174
  static async getHooksForType(state, hookType) {
9619
- const hookIds = this.getActiveHookIds(state);
9620
- if (hookIds.length === 0) return [];
9621
- const hooksRegistry = state.thread.instance.hooks();
9622
- if (!hooksRegistry) return [];
9623
- const executors = [];
9624
- for (const hookId of hookIds) {
9625
- const hookLoader = hooksRegistry[hookId];
9626
- if (!hookLoader) continue;
9627
- try {
9628
- const hookDef = await hookLoader();
9629
- if (hookDef && hookDef.hook === hookType) {
9630
- executors.push(hookDef.execute);
9631
- }
9632
- } catch (error) {
9633
- console.error(`[Hooks] Failed to load hook '${hookId}':`, error);
9634
- }
9635
- }
9636
- return executors;
10175
+ return await resolveHooksForPrompt(
10176
+ {
10177
+ host: state.thread.instance,
10178
+ agentName: state.agentConfig.id,
10179
+ agent: state.agentConfig,
10180
+ promptName: state.prompt?.id,
10181
+ prompt: state.prompt,
10182
+ isOptionalRelationshipEnabled: (envName) => this.isOptionalToolEnabled(state, envName)
10183
+ },
10184
+ hookType
10185
+ );
9637
10186
  }
9638
10187
  // ==========================================================================
9639
10188
  // Hook Runner Methods
@@ -9651,9 +10200,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9651
10200
  if (executors.length === 0) return messages;
9652
10201
  let result = messages;
9653
10202
  const threadState = ThreadStateImpl.fromFlowState(state);
9654
- for (const executor of executors) {
10203
+ for (const { execute, context } of executors) {
9655
10204
  try {
9656
- result = await executor(threadState, result);
10205
+ result = await execute(threadState, result, context);
9657
10206
  } catch (error) {
9658
10207
  if (error && typeof error === "object" && error.block === true) {
9659
10208
  throw new HookBlockedExecutionError(error instanceof Error ? error.message : String(error));
@@ -9671,9 +10220,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9671
10220
  if (executors.length === 0) return systemMessage;
9672
10221
  let result = systemMessage;
9673
10222
  const threadState = ThreadStateImpl.fromFlowState(state);
9674
- for (const executor of executors) {
10223
+ for (const { execute, context } of executors) {
9675
10224
  try {
9676
- const next = await executor(threadState, result);
10225
+ const next = await execute(threadState, result, context);
9677
10226
  if (next !== null && next !== void 0) {
9678
10227
  result = next;
9679
10228
  }
@@ -9690,9 +10239,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9690
10239
  const executors = await this.getHooksForType(state, "after_subagent_created");
9691
10240
  if (executors.length === 0) return;
9692
10241
  const threadState = ThreadStateImpl.fromFlowState(state);
9693
- for (const executor of executors) {
10242
+ for (const { execute, context } of executors) {
9694
10243
  try {
9695
- await executor(threadState, childState);
10244
+ await execute(threadState, childState, context);
9696
10245
  } catch (error) {
9697
10246
  console.error("[Hooks] \u2717 Error running after_subagent_created hook:", error);
9698
10247
  }
@@ -9706,20 +10255,27 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9706
10255
  * @param message - Message object to be created
9707
10256
  * @returns Processed message (or original if hook doesn't exist/fails)
9708
10257
  */
9709
- static async runBeforeCreateMessageHook(state, message) {
10258
+ static async runBeforeCreateMessageHook(state, message, threadStateOverride, options) {
9710
10259
  const executors = await this.getHooksForType(state, "before_create_message");
9711
10260
  if (executors.length === 0) return message;
9712
10261
  let result = message;
9713
- const threadState = ThreadStateImpl.fromFlowState(state);
9714
- for (const executor of executors) {
10262
+ const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
10263
+ for (const { execute, context } of executors) {
9715
10264
  try {
9716
- const hooked = await executor(threadState, _FlowEngine.rowToHookMessage(result));
10265
+ const hooked = await execute(
10266
+ threadState,
10267
+ _FlowEngine.rowToHookMessage(result),
10268
+ context
10269
+ );
9717
10270
  result = {
9718
10271
  ...result,
9719
10272
  ...hooked,
9720
10273
  attachments: hooked.attachments === void 0 ? result.attachments : hooked.attachments === null ? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments)
9721
10274
  };
9722
10275
  } catch (error) {
10276
+ if (options?.propagateHookErrors) {
10277
+ throw error;
10278
+ }
9723
10279
  console.error("[Hooks] \u2717 Error running before_create_message hook:", error);
9724
10280
  }
9725
10281
  }
@@ -9732,13 +10288,13 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9732
10288
  * @param state - Flow state with execution context
9733
10289
  * @param message - The created message object
9734
10290
  */
9735
- static async runAfterCreateMessageHook(state, message) {
10291
+ static async runAfterCreateMessageHook(state, message, threadStateOverride) {
9736
10292
  const executors = await this.getHooksForType(state, "after_create_message");
9737
10293
  if (executors.length === 0) return;
9738
- const threadState = ThreadStateImpl.fromFlowState(state);
9739
- for (const executor of executors) {
10294
+ const threadState = threadStateOverride ?? ThreadStateImpl.fromFlowState(state);
10295
+ for (const { execute, context } of executors) {
9740
10296
  try {
9741
- await executor(threadState, _FlowEngine.rowToHookMessage(message));
10297
+ await execute(threadState, _FlowEngine.rowToHookMessage(message), context);
9742
10298
  } catch (error) {
9743
10299
  console.error("[Hooks] \u2717 Error running after_create_message hook:", error);
9744
10300
  }
@@ -9779,9 +10335,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9779
10335
  if (executors.length === 0) return updates;
9780
10336
  let result = updates;
9781
10337
  const threadState = ThreadStateImpl.fromFlowState(state);
9782
- for (const executor of executors) {
10338
+ for (const { execute, context } of executors) {
9783
10339
  try {
9784
- result = await executor(threadState, messageId, result);
10340
+ result = await execute(threadState, messageId, result, context);
9785
10341
  } catch (error) {
9786
10342
  console.error("[Hooks] \u2717 Error running before_update_message hook:", error);
9787
10343
  }
@@ -9799,9 +10355,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9799
10355
  const executors = await this.getHooksForType(state, "after_update_message");
9800
10356
  if (executors.length === 0) return;
9801
10357
  const threadState = ThreadStateImpl.fromFlowState(state);
9802
- for (const executor of executors) {
10358
+ for (const { execute, context } of executors) {
9803
10359
  try {
9804
- await executor(threadState, _FlowEngine.rowToHookMessage(message));
10360
+ await execute(threadState, _FlowEngine.rowToHookMessage(message), context);
9805
10361
  } catch (error) {
9806
10362
  console.error("[Hooks] \u2717 Error running after_update_message hook:", error);
9807
10363
  }
@@ -9821,9 +10377,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9821
10377
  if (executors.length === 0) return toolResult;
9822
10378
  let result = toolResult;
9823
10379
  const threadState = ThreadStateImpl.fromFlowState(state);
9824
- for (const executor of executors) {
10380
+ for (const { execute, context } of executors) {
9825
10381
  try {
9826
- result = await executor(threadState, toolCall, result);
10382
+ result = await execute(threadState, toolCall, result, context);
9827
10383
  } catch (error) {
9828
10384
  console.error("[Hooks] \u2717 Error running before_store_tool_result hook:", error);
9829
10385
  }
@@ -9843,10 +10399,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9843
10399
  if (executors.length === 0) return toolResult;
9844
10400
  let result = toolResult;
9845
10401
  const threadState = ThreadStateImpl.fromFlowState(state);
9846
- for (const executor of executors) {
10402
+ for (const { execute, context } of executors) {
9847
10403
  if (result === null) break;
9848
10404
  try {
9849
- result = await executor(threadState, toolCall, result);
10405
+ result = await execute(threadState, toolCall, result, context);
9850
10406
  } catch (error) {
9851
10407
  console.error("[Hooks] \u2717 Error running after_tool_call_success hook:", error);
9852
10408
  }
@@ -9866,10 +10422,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9866
10422
  if (executors.length === 0) return toolResult;
9867
10423
  let result = toolResult;
9868
10424
  const threadState = ThreadStateImpl.fromFlowState(state);
9869
- for (const executor of executors) {
10425
+ for (const { execute, context } of executors) {
9870
10426
  if (result === null) break;
9871
10427
  try {
9872
- result = await executor(threadState, toolCall, result);
10428
+ result = await execute(threadState, toolCall, result, context);
9873
10429
  } catch (error) {
9874
10430
  console.error("[Hooks] \u2717 Error running after_tool_call_failure hook:", error);
9875
10431
  }
@@ -9899,9 +10455,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9899
10455
  const executors = await this.getHooksForType(state, "tool_call_started");
9900
10456
  if (executors.length === 0) return;
9901
10457
  const threadState = ThreadStateImpl.fromFlowState(state);
9902
- for (const executor of executors) {
10458
+ for (const { execute, context } of executors) {
9903
10459
  try {
9904
- await executor(threadState, toolCall, progress);
10460
+ await execute(threadState, toolCall, progress, context);
9905
10461
  } catch (error) {
9906
10462
  console.error("[Hooks] \u2717 Error running tool_call_started hook:", error);
9907
10463
  }
@@ -9929,9 +10485,9 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
9929
10485
  const executors = await this.getHooksForType(state, "tool_call_done");
9930
10486
  if (executors.length === 0) return;
9931
10487
  const threadState = ThreadStateImpl.fromFlowState(state);
9932
- for (const executor of executors) {
10488
+ for (const { execute, context } of executors) {
9933
10489
  try {
9934
- await executor(threadState, toolCall, toolResult);
10490
+ await execute(threadState, toolCall, toolResult, context);
9935
10491
  } catch (error) {
9936
10492
  console.error("[Hooks] \u2717 Error running tool_call_done hook:", error);
9937
10493
  }
@@ -10236,7 +10792,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10236
10792
  }
10237
10793
  existingPathMessages.add(content);
10238
10794
  const attachmentsJson = JSON.stringify([ref]);
10239
- const silentMessage = {
10795
+ let silentMessage = {
10240
10796
  id: crypto.randomUUID(),
10241
10797
  role: sourceMessage.role,
10242
10798
  content,
@@ -10247,6 +10803,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10247
10803
  parent_id: parentId,
10248
10804
  depth
10249
10805
  };
10806
+ silentMessage = await this.runBeforeCreateMessageHook(state, silentMessage);
10250
10807
  await state.storage.sql.exec(
10251
10808
  `
10252
10809
  INSERT INTO messages (
@@ -10257,15 +10814,16 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10257
10814
  silentMessage.id,
10258
10815
  silentMessage.role,
10259
10816
  silentMessage.content,
10260
- attachmentsJson,
10261
- 1,
10817
+ silentMessage.attachments ?? null,
10818
+ silentMessage.silent ? 1 : 0,
10262
10819
  silentMessage.created_at,
10263
- silentMessage.status,
10820
+ silentMessage.status ?? "completed",
10264
10821
  silentMessage.parent_id,
10265
10822
  silentMessage.depth,
10266
10823
  currentPrompt
10267
10824
  );
10268
10825
  state.messageHistory.push(silentMessage);
10826
+ await this.runAfterCreateMessageHook(state, silentMessage);
10269
10827
  if (state.emitMessage) {
10270
10828
  state.emitMessage(silentMessage);
10271
10829
  }
@@ -10804,7 +11362,7 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10804
11362
  */
10805
11363
  static async loadMessageHistory(state) {
10806
11364
  const storage = state.storage;
10807
- const thread = state.thread;
11365
+ state.thread;
10808
11366
  const includeParentHistory = state.prompt.include_chat ?? true;
10809
11367
  const includeToolCalls = state.prompt.include_past_tools ?? true;
10810
11368
  try {
@@ -10840,8 +11398,11 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10840
11398
  rows = cursor.toArray();
10841
11399
  }
10842
11400
  if (state.extraMessages && state.extraMessages.length > 0) {
10843
- rows.push(...state.extraMessages);
10844
- rows.sort((a, b) => a.created_at - b.created_at || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
11401
+ const persistedIds = new Set(rows.map((row) => row.id));
11402
+ rows.push(
11403
+ ...state.extraMessages.filter((entry) => !persistedIds.has(entry.id))
11404
+ );
11405
+ rows.sort((a, b) => a.created_at - b.created_at);
10845
11406
  }
10846
11407
  const messages = rows.map((row) => ({
10847
11408
  id: row.id,
@@ -10864,43 +11425,77 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
10864
11425
  reasoning_content: row.reasoning_content,
10865
11426
  reasoning_details: row.reasoning_details
10866
11427
  }));
10867
- return this.runFilterMessagesHook(
10868
- thread.instance,
10869
- state,
10870
- this.filterOrphanedToolCalls(messages)
10871
- );
11428
+ return messages;
10872
11429
  } catch (error) {
10873
11430
  console.error("Error loading message history:", error);
10874
11431
  return [];
10875
11432
  }
10876
11433
  }
10877
11434
  /**
10878
- * Run the filter_messages hook if it exists
10879
- * This hook allows developers to modify SQL row data before transformation
10880
- *
10881
- * @param thread - Thread instance with hooks registry
10882
- * @param state - Flow state for hook context
10883
- * @param rows - SQL rows from messages table
10884
- * @returns Processed rows (or original if hook doesn't exist/fails)
11435
+ * Build request-context rows from persisted history.
11436
+ * Mutations and synthetic messages stay in the request context.
10885
11437
  */
10886
11438
  static async runFilterMessagesHook(thread, state, messages) {
10887
11439
  const executors = await this.getHooksForType(state, "filter_messages");
10888
11440
  if (executors.length === 0) return messages;
10889
11441
  let result = messages;
10890
11442
  const threadState = ThreadStateImpl.fromFlowState(state);
10891
- for (const executor of executors) {
11443
+ for (const { execute, context } of executors) {
10892
11444
  try {
10893
11445
  const byId = /* @__PURE__ */ new Map();
10894
11446
  for (const row of result) if (row.id) byId.set(row.id, row);
10895
11447
  const hookView = result.map((row) => _FlowEngine.rowToHookMessage(row));
10896
- const kept = await executor(threadState, hookView);
10897
- result = kept.map((m) => m.id && byId.get(m.id) || null).filter((row) => row !== null);
11448
+ const kept = await execute(threadState, hookView, context);
11449
+ result = kept.map((hookedMessage) => {
11450
+ const original = hookedMessage.id ? byId.get(hookedMessage.id) : void 0;
11451
+ if (original) {
11452
+ return _FlowEngine.applyHookMessageToRow(original, hookedMessage);
11453
+ }
11454
+ return _FlowEngine.hookMessageToContextRow(hookedMessage);
11455
+ });
10898
11456
  } catch (error) {
10899
11457
  console.error("[Hooks] \u2717 Error running filter_messages hook:", error);
10900
11458
  }
10901
11459
  }
10902
11460
  return result;
10903
11461
  }
11462
+ /**
11463
+ * Copy a persisted row and apply hook edits. The original live-history
11464
+ * object is left unchanged.
11465
+ */
11466
+ static applyHookMessageToRow(original, hooked) {
11467
+ const attachments = hooked.attachments === void 0 ? original.attachments : hooked.attachments === null ? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments);
11468
+ return {
11469
+ ...original,
11470
+ role: hooked.role ?? original.role,
11471
+ content: hooked.content !== void 0 ? hooked.content : original.content,
11472
+ name: hooked.name !== void 0 ? hooked.name : original.name,
11473
+ tool_calls: hooked.tool_calls !== void 0 ? hooked.tool_calls : original.tool_calls,
11474
+ tool_call_id: hooked.tool_call_id !== void 0 ? hooked.tool_call_id : original.tool_call_id,
11475
+ silent: hooked.silent !== void 0 ? hooked.silent : original.silent,
11476
+ attachments
11477
+ };
11478
+ }
11479
+ /**
11480
+ * Convert a synthetic hook message into a request-context-only builder row.
11481
+ */
11482
+ static hookMessageToContextRow(hooked) {
11483
+ const attachments = hooked.attachments === void 0 || hooked.attachments === null ? hooked.attachments ?? null : typeof hooked.attachments === "string" ? hooked.attachments : JSON.stringify(hooked.attachments);
11484
+ return {
11485
+ id: hooked.id || crypto.randomUUID(),
11486
+ role: hooked.role,
11487
+ content: hooked.content ?? null,
11488
+ name: hooked.name ?? null,
11489
+ tool_calls: typeof hooked.tool_calls === "string" ? hooked.tool_calls : null,
11490
+ tool_call_id: hooked.tool_call_id ?? null,
11491
+ created_at: hooked.created_at ?? Date.now() * TIMESTAMP_MULTIPLIER,
11492
+ parent_id: hooked.parent_id ?? null,
11493
+ depth: hooked.depth ?? 0,
11494
+ silent: hooked.silent ?? false,
11495
+ status: "completed",
11496
+ attachments
11497
+ };
11498
+ }
10904
11499
  /**
10905
11500
  * Log an error to the logs table (best-effort)
10906
11501
  * Creates a log entry with error details but no associated message_id
@@ -11044,6 +11639,35 @@ ${errorStack}` : ""}`;
11044
11639
  });
11045
11640
 
11046
11641
  // src/agents/utilities.ts
11642
+ var utilities_exports = {};
11643
+ __export(utilities_exports, {
11644
+ cat: () => cat,
11645
+ emitThreadEvent: () => emitThreadEvent,
11646
+ exists: () => exists,
11647
+ find: () => find,
11648
+ forceTurn: () => forceTurn,
11649
+ getFileStats: () => getFileStats,
11650
+ getMessages: () => getMessages,
11651
+ getThumbnail: () => getThumbnail,
11652
+ grep: () => grep,
11653
+ head: () => head,
11654
+ injectMessage: () => injectMessage,
11655
+ linkFile: () => linkFile,
11656
+ mkdir: () => mkdir,
11657
+ move: () => move,
11658
+ queueTool: () => queueTool,
11659
+ readFile: () => readFile,
11660
+ readdir: () => readdir,
11661
+ reloadHistory: () => reloadHistory,
11662
+ rename: () => rename,
11663
+ rmdir: () => rmdir,
11664
+ stat: () => stat,
11665
+ tail: () => tail,
11666
+ unlink: () => unlink,
11667
+ updateThread: () => updateThread,
11668
+ writeFile: () => writeFile,
11669
+ writeImage: () => writeImage
11670
+ });
11047
11671
  function queueTool(flow, toolName, args = {}) {
11048
11672
  if (flow.currentToolUses !== void 0) {
11049
11673
  if (!flow.currentToolUses.includes(toolName)) {
@@ -11071,7 +11695,7 @@ async function injectMessage(flow, options) {
11071
11695
  const timestamp = Date.now() * TIMESTAMP_MULTIPLIER;
11072
11696
  const silent = options.silent ?? false;
11073
11697
  const forceTopLevel = options.forceTopLevel ?? false;
11074
- const message = {
11698
+ let message = {
11075
11699
  id: messageId,
11076
11700
  role: options.role,
11077
11701
  content: options.content,
@@ -11085,41 +11709,52 @@ async function injectMessage(flow, options) {
11085
11709
  metadata: options.metadata
11086
11710
  };
11087
11711
  if (options.beforeMessageId) {
11088
- const cursor = await flow.storage.sql.exec(
11089
- `SELECT created_at FROM messages WHERE id = ?`,
11090
- options.beforeMessageId
11712
+ const liveTarget = flow.messageHistory.find(
11713
+ (entry) => entry.id === options.beforeMessageId
11091
11714
  );
11092
- const rows = cursor.toArray();
11093
- const targetMessage = rows[0];
11094
- if (!targetMessage) {
11095
- throw new Error(
11096
- `Message with id ${options.beforeMessageId} not found`
11715
+ let targetCreatedAt = liveTarget?.created_at;
11716
+ if (targetCreatedAt === void 0) {
11717
+ const cursor = await flow.storage.sql.exec(
11718
+ `SELECT created_at FROM messages WHERE id = ?`,
11719
+ options.beforeMessageId
11097
11720
  );
11721
+ const rows = cursor.toArray();
11722
+ const targetMessage = rows[0];
11723
+ if (!targetMessage) {
11724
+ throw new Error(
11725
+ `Message with id ${options.beforeMessageId} not found`
11726
+ );
11727
+ }
11728
+ targetCreatedAt = targetMessage.created_at;
11098
11729
  }
11099
- message.created_at = targetMessage.created_at - 1;
11730
+ message.created_at = targetCreatedAt - 1;
11100
11731
  }
11101
11732
  const currentPrompt = flow.promptPath.length > 0 ? flow.promptPath[flow.promptPath.length - 1] : null;
11102
- const metadataJson = options.metadata ? JSON.stringify(options.metadata) : null;
11733
+ const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
11734
+ message = await FlowEngine2.runBeforeCreateMessageHook(flow, message, void 0, {
11735
+ propagateHookErrors: true
11736
+ });
11737
+ const metadataJson = message.metadata === void 0 ? null : JSON.stringify(message.metadata);
11103
11738
  await flow.storage.sql.exec(
11104
11739
  `INSERT INTO messages (id, role, content, name, tool_call_id, created_at, status, silent, parent_id, depth, metadata, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
11105
- messageId,
11740
+ message.id,
11106
11741
  message.role,
11107
11742
  message.content,
11108
11743
  message.name,
11109
11744
  message.tool_call_id,
11110
11745
  message.created_at,
11111
11746
  message.status,
11112
- silent ? 1 : 0,
11747
+ message.silent ? 1 : 0,
11113
11748
  message.parent_id,
11114
11749
  message.depth,
11115
11750
  metadataJson,
11116
11751
  currentPrompt
11117
11752
  );
11118
- if (!silent) {
11119
- flow.messageHistory.push(message);
11120
- } else {
11121
- flow.messageHistory = await reloadHistory(flow);
11753
+ appendLiveExecutionHistory(flow, message, options.beforeMessageId);
11754
+ if (flow.prompt.include_chat === false || message.parent_id !== null || (message.depth ?? 0) > 0) {
11755
+ appendInjectedContextMessage(flow.extraMessages, message);
11122
11756
  }
11757
+ await FlowEngine2.runAfterCreateMessageHook(flow, message);
11123
11758
  if (flow.emitMessage) {
11124
11759
  flow.emitMessage(message);
11125
11760
  }
@@ -11128,6 +11763,42 @@ async function injectMessage(flow, options) {
11128
11763
  }
11129
11764
  return message;
11130
11765
  }
11766
+ function appendLiveExecutionHistory(flow, message, beforeMessageId) {
11767
+ const history = flow.messageHistory;
11768
+ const existing = history.findIndex((entry) => entry.id === message.id);
11769
+ if (existing >= 0) {
11770
+ history.splice(existing, 1);
11771
+ }
11772
+ if (beforeMessageId) {
11773
+ const targetIndex = history.findIndex((entry) => entry.id === beforeMessageId);
11774
+ if (targetIndex >= 0) {
11775
+ history.splice(targetIndex, 0, message);
11776
+ return;
11777
+ }
11778
+ }
11779
+ const insertAt = history.findIndex(
11780
+ (entry) => entry.created_at > message.created_at
11781
+ );
11782
+ if (insertAt === -1) {
11783
+ history.push(message);
11784
+ return;
11785
+ }
11786
+ history.splice(insertAt, 0, message);
11787
+ }
11788
+ function appendInjectedContextMessage(extraMessages, message) {
11789
+ const existing = extraMessages.findIndex((entry) => entry.id === message.id);
11790
+ if (existing >= 0) {
11791
+ extraMessages.splice(existing, 1);
11792
+ }
11793
+ const insertAt = extraMessages.findIndex(
11794
+ (entry) => entry.created_at > message.created_at
11795
+ );
11796
+ if (insertAt === -1) {
11797
+ extraMessages.push(message);
11798
+ return;
11799
+ }
11800
+ extraMessages.splice(insertAt, 0, message);
11801
+ }
11131
11802
  async function getMessages(flow, limit = 100, offset = 0, order = "asc") {
11132
11803
  const orderClause = order === "desc" ? "DESC" : "ASC";
11133
11804
  const cursor = await flow.storage.sql.exec(
@@ -17607,649 +18278,336 @@ var migration48 = {
17607
18278
  )
17608
18279
  `);
17609
18280
  sql.exec(`INSERT INTO api_keys SELECT * FROM api_keys_0012_backup`);
17610
- sql.exec(`CREATE INDEX idx_api_keys_key_hash ON api_keys(key_hash)`);
17611
- sql.exec(`CREATE INDEX idx_api_keys_user_id ON api_keys(user_id)`);
17612
- sql.exec(`CREATE INDEX idx_api_keys_key_prefix ON api_keys(key_prefix)`);
17613
- sql.exec(`CREATE INDEX idx_api_keys_platform_key_id ON api_keys(platform_key_id)`);
17614
- sql.exec(`CREATE INDEX idx_api_keys_replica_active ON api_keys(replica_active)`);
17615
- sql.exec(`
17616
- CREATE TABLE sessions (
17617
- id TEXT PRIMARY KEY NOT NULL,
17618
- user_id TEXT NOT NULL,
17619
- token_hash TEXT NOT NULL,
17620
- expires_at INTEGER NOT NULL,
17621
- created_at INTEGER DEFAULT (strftime('%s', 'now')),
17622
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
17623
- )
17624
- `);
17625
- sql.exec(`INSERT INTO sessions SELECT * FROM sessions_0012_backup`);
17626
- sql.exec(`CREATE INDEX idx_sessions_token_hash ON sessions(token_hash)`);
17627
- sql.exec(`CREATE INDEX idx_sessions_user_id ON sessions(user_id)`);
17628
- sql.exec(`CREATE INDEX idx_sessions_expires_at ON sessions(expires_at)`);
17629
- sql.exec(`DROP TABLE oauth_accounts_0012_backup`);
17630
- sql.exec(`DROP TABLE api_keys_0012_backup`);
17631
- sql.exec(`DROP TABLE sessions_0012_backup`);
17632
- sql.exec(`
17633
- INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '12')
17634
- `);
17635
- }
17636
- };
17637
-
17638
- // src/durable-objects/agentbuilder-migrations/0013_add_thread_activity.ts
17639
- var migration49 = {
17640
- version: 13,
17641
- async up(sql) {
17642
- sql.exec(`ALTER TABLE threads ADD COLUMN last_activity_at INTEGER`);
17643
- sql.exec(`ALTER TABLE threads ADD COLUMN executing_since INTEGER`);
17644
- sql.exec(
17645
- `CREATE INDEX IF NOT EXISTS idx_threads_last_activity_at ON threads(last_activity_at)`
17646
- );
17647
- sql.exec(`UPDATE threads SET last_activity_at = created_at`);
17648
- sql.exec(`
17649
- INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '13')
17650
- `);
17651
- }
17652
- };
17653
-
17654
- // src/durable-objects/agentbuilder-migrations/0014_thread_activity_scale.ts
17655
- var migration50 = {
17656
- version: 14,
17657
- async up(sql) {
17658
- sql.exec(`ALTER TABLE threads ADD COLUMN last_message_preview TEXT`);
17659
- sql.exec(
17660
- `CREATE INDEX IF NOT EXISTS idx_threads_executing_since
17661
- ON threads(executing_since) WHERE executing_since IS NOT NULL`
17662
- );
17663
- sql.exec(
17664
- `UPDATE threads SET last_activity_at = created_at WHERE last_activity_at IS NULL`
17665
- );
17666
- sql.exec(`
17667
- INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '14')
17668
- `);
17669
- }
17670
- };
17671
-
17672
- // src/durable-objects/agentbuilder-migrations/0015_clear_envelope_previews.ts
17673
- var migration51 = {
17674
- version: 15,
17675
- async up(sql) {
17676
- sql.exec(
17677
- `UPDATE threads SET last_message_preview = NULL
17678
- WHERE last_message_preview LIKE 'Subagent (reference: %'`
17679
- );
17680
- sql.exec(`
17681
- INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '15')
17682
- `);
17683
- }
17684
- };
17685
-
17686
- // src/durable-objects/agentbuilder-migrations/0016_add_origin_agent.ts
17687
- var migration52 = {
17688
- version: 16,
17689
- async up(sql) {
17690
- sql.exec(`ALTER TABLE threads ADD COLUMN origin_agent_name TEXT`);
17691
- sql.exec(
17692
- `CREATE INDEX IF NOT EXISTS idx_threads_origin_agent_name ON threads(origin_agent_name)`
17693
- );
17694
- sql.exec(`UPDATE threads SET origin_agent_name = agent_name`);
17695
- sql.exec(`
17696
- INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '16')
17697
- `);
17698
- }
17699
- };
17700
-
17701
- // src/durable-objects/agentbuilder-migrations/0017_add_thread_message_recency.ts
17702
- var migration53 = {
17703
- version: 17,
17704
- async up(sql) {
17705
- sql.exec(`ALTER TABLE threads ADD COLUMN last_message_at INTEGER`);
17706
- sql.exec(`UPDATE threads SET last_message_at = created_at`);
17707
- sql.exec(`
17708
- INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '17')
17709
- `);
17710
- }
17711
- };
17712
-
17713
- // src/durable-objects/agentbuilder-migrations/0018_auth_security.ts
17714
- var migration54 = {
17715
- version: 18,
17716
- async up(sql) {
17717
- sql.exec(`
17718
- CREATE TABLE IF NOT EXISTS super_admin_sessions (
17719
- token_hash TEXT PRIMARY KEY,
17720
- created_at INTEGER NOT NULL,
17721
- expires_at INTEGER NOT NULL
17722
- )
17723
- `);
17724
- sql.exec(`
17725
- CREATE INDEX IF NOT EXISTS idx_super_admin_sessions_expires
17726
- ON super_admin_sessions(expires_at)
17727
- `);
17728
- sql.exec(`
17729
- CREATE TABLE IF NOT EXISTS auth_rate_limits (
17730
- bucket_key TEXT PRIMARY KEY,
17731
- attempts INTEGER NOT NULL,
17732
- reset_at INTEGER NOT NULL
17733
- )
17734
- `);
17735
- sql.exec(`
17736
- CREATE INDEX IF NOT EXISTS idx_auth_rate_limits_reset
17737
- ON auth_rate_limits(reset_at)
17738
- `);
17739
- sql.exec(`
17740
- INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '18')
17741
- `);
17742
- }
17743
- };
17744
-
17745
- // src/durable-objects/agentbuilder-migrations/0019_scope_skills.ts
17746
- var migration55 = {
17747
- version: 19,
17748
- async up(sql) {
17749
- sql.exec(`
17750
- CREATE TABLE skills_0019 (
17751
- owner_id TEXT NOT NULL,
17752
- name TEXT NOT NULL,
17753
- description TEXT NOT NULL,
17754
- version TEXT,
17755
- enabled INTEGER NOT NULL DEFAULT 1,
17756
- shared INTEGER NOT NULL DEFAULT 0,
17757
- created_at INTEGER DEFAULT (strftime('%s', 'now')),
17758
- updated_at INTEGER DEFAULT (strftime('%s', 'now')),
17759
- PRIMARY KEY (owner_id, name)
17760
- )
17761
- `);
17762
- sql.exec(`
17763
- INSERT INTO skills_0019 (
17764
- owner_id, name, description, version, enabled, shared, created_at, updated_at
17765
- )
17766
- SELECT '__instance_shared__', name, description, version, enabled, 1, created_at, updated_at
17767
- FROM skills
17768
- `);
17769
- sql.exec(`
17770
- CREATE TABLE skill_files_0019 (
17771
- owner_id TEXT NOT NULL,
17772
- skill_name TEXT NOT NULL,
17773
- path TEXT NOT NULL,
17774
- content TEXT NOT NULL,
17775
- size INTEGER NOT NULL,
17776
- PRIMARY KEY (owner_id, skill_name, path),
17777
- FOREIGN KEY (owner_id, skill_name) REFERENCES skills_0019(owner_id, name) ON DELETE CASCADE
17778
- )
17779
- `);
17780
- sql.exec(`
17781
- INSERT INTO skill_files_0019 (owner_id, skill_name, path, content, size)
17782
- SELECT '__instance_shared__', skill_name, path, content, size FROM skill_files
17783
- `);
17784
- sql.exec(`DROP TABLE skill_files`);
17785
- sql.exec(`DROP TABLE skills`);
17786
- sql.exec(`ALTER TABLE skills_0019 RENAME TO skills`);
17787
- sql.exec(`ALTER TABLE skill_files_0019 RENAME TO skill_files`);
17788
- sql.exec(`CREATE INDEX idx_skills_owner_enabled ON skills(owner_id, enabled)`);
17789
- sql.exec(`CREATE INDEX idx_skills_shared_enabled ON skills(shared, enabled)`);
17790
- sql.exec(`CREATE INDEX idx_skill_files_skill ON skill_files(owner_id, skill_name)`);
17791
- sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '19')`);
17792
- }
17793
- };
17794
-
17795
- // src/durable-objects/agentbuilder-migrations/0020_websocket_tickets.ts
17796
- var migration56 = {
17797
- version: 20,
17798
- async up(sql) {
17799
- sql.exec(`
17800
- CREATE TABLE websocket_tickets (
17801
- ticket_hash TEXT PRIMARY KEY,
17802
- pathname TEXT NOT NULL,
17803
- auth_json TEXT NOT NULL,
17804
- created_at INTEGER NOT NULL,
17805
- expires_at INTEGER NOT NULL
17806
- )
17807
- `);
17808
- sql.exec(`
17809
- CREATE INDEX idx_websocket_tickets_expires
17810
- ON websocket_tickets(expires_at)
17811
- `);
17812
- sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '20')`);
17813
- }
17814
- };
17815
-
17816
- // src/durable-objects/agentbuilder-migrations/0021_platform_handoff_jtis.ts
17817
- var migration57 = {
17818
- version: 21,
17819
- async up(sql) {
18281
+ sql.exec(`CREATE INDEX idx_api_keys_key_hash ON api_keys(key_hash)`);
18282
+ sql.exec(`CREATE INDEX idx_api_keys_user_id ON api_keys(user_id)`);
18283
+ sql.exec(`CREATE INDEX idx_api_keys_key_prefix ON api_keys(key_prefix)`);
18284
+ sql.exec(`CREATE INDEX idx_api_keys_platform_key_id ON api_keys(platform_key_id)`);
18285
+ sql.exec(`CREATE INDEX idx_api_keys_replica_active ON api_keys(replica_active)`);
17820
18286
  sql.exec(`
17821
- CREATE TABLE platform_handoff_jtis (
17822
- jti TEXT PRIMARY KEY,
17823
- consumed_at INTEGER NOT NULL,
17824
- expires_at INTEGER NOT NULL
18287
+ CREATE TABLE sessions (
18288
+ id TEXT PRIMARY KEY NOT NULL,
18289
+ user_id TEXT NOT NULL,
18290
+ token_hash TEXT NOT NULL,
18291
+ expires_at INTEGER NOT NULL,
18292
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
18293
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
17825
18294
  )
17826
18295
  `);
18296
+ sql.exec(`INSERT INTO sessions SELECT * FROM sessions_0012_backup`);
18297
+ sql.exec(`CREATE INDEX idx_sessions_token_hash ON sessions(token_hash)`);
18298
+ sql.exec(`CREATE INDEX idx_sessions_user_id ON sessions(user_id)`);
18299
+ sql.exec(`CREATE INDEX idx_sessions_expires_at ON sessions(expires_at)`);
18300
+ sql.exec(`DROP TABLE oauth_accounts_0012_backup`);
18301
+ sql.exec(`DROP TABLE api_keys_0012_backup`);
18302
+ sql.exec(`DROP TABLE sessions_0012_backup`);
17827
18303
  sql.exec(`
17828
- CREATE INDEX idx_platform_handoff_jtis_expires
17829
- ON platform_handoff_jtis(expires_at)
18304
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '12')
17830
18305
  `);
17831
- sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '21')`);
17832
- }
17833
- };
17834
-
17835
- // src/durable-objects/agentbuilder-migrations/index.ts
17836
- var migrations2 = [migration37, migration38, migration39, migration40, migration41, migration42, migration43, migration44, migration45, migration46, migration47, migration48, migration49, migration50, migration51, migration52, migration53, migration54, migration55, migration56, migration57];
17837
- var LATEST_SCHEMA_VERSION2 = migrations2.reduce(
17838
- (max, migration58) => Math.max(max, migration58.version),
17839
- 0
17840
- );
17841
-
17842
- // src/utils/crypto.ts
17843
- var CryptoUtil = class {
17844
- static algorithm = "aes-256-gcm";
17845
- /**
17846
- * Encrypt a string value
17847
- */
17848
- static async encrypt(text, key) {
17849
- const encoder = new TextEncoder();
17850
- const data = encoder.encode(text);
17851
- const cryptoKey = await crypto.subtle.importKey(
17852
- "raw",
17853
- hexToBuffer(key),
17854
- { name: "AES-GCM" },
17855
- false,
17856
- ["encrypt"]
17857
- );
17858
- const iv = crypto.getRandomValues(new Uint8Array(12));
17859
- const encrypted = await crypto.subtle.encrypt(
17860
- {
17861
- name: "AES-GCM",
17862
- iv
17863
- },
17864
- cryptoKey,
17865
- data
17866
- );
17867
- const combined = new Uint8Array(iv.length + encrypted.byteLength);
17868
- combined.set(iv);
17869
- combined.set(new Uint8Array(encrypted), iv.length);
17870
- return bufferToBase64(combined);
17871
- }
17872
- /**
17873
- * Decrypt a string value
17874
- */
17875
- static async decrypt(encryptedText, key) {
17876
- const combined = base64ToBuffer2(encryptedText);
17877
- const iv = combined.slice(0, 12);
17878
- const encrypted = combined.slice(12);
17879
- const cryptoKey = await crypto.subtle.importKey(
17880
- "raw",
17881
- hexToBuffer(key),
17882
- { name: "AES-GCM" },
17883
- false,
17884
- ["decrypt"]
17885
- );
17886
- const decrypted = await crypto.subtle.decrypt(
17887
- {
17888
- name: "AES-GCM",
17889
- iv
17890
- },
17891
- cryptoKey,
17892
- encrypted
17893
- );
17894
- const decoder = new TextDecoder();
17895
- return decoder.decode(decrypted);
17896
18306
  }
17897
18307
  };
17898
- function hexToBuffer(hex) {
17899
- const bytes = new Uint8Array(hex.length / 2);
17900
- for (let i = 0; i < hex.length; i += 2) {
17901
- bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
17902
- }
17903
- return bytes;
17904
- }
17905
- function bufferToBase64(buffer) {
17906
- let binary = "";
17907
- for (let i = 0; i < buffer.byteLength; i++) {
17908
- binary += String.fromCharCode(buffer[i]);
17909
- }
17910
- return btoa(binary);
17911
- }
17912
- function base64ToBuffer2(base64) {
17913
- const binary = atob(base64);
17914
- const bytes = new Uint8Array(binary.length);
17915
- for (let i = 0; i < binary.length; i++) {
17916
- bytes[i] = binary.charCodeAt(i);
17917
- }
17918
- return bytes;
17919
- }
17920
- async function encryptEnvs(env, encryptionKey) {
17921
- const plaintext = JSON.stringify(env);
17922
- return CryptoUtil.encrypt(plaintext, encryptionKey);
17923
- }
17924
- async function decryptEnvs(encrypted, encryptionKey) {
17925
- const plaintext = await CryptoUtil.decrypt(encrypted, encryptionKey);
17926
- const parsed = JSON.parse(plaintext);
17927
- if (!parsed || typeof parsed !== "object") {
17928
- return {};
17929
- }
17930
- const result = {};
17931
- for (const [key, value] of Object.entries(parsed)) {
17932
- if (value === void 0 || value === null) continue;
17933
- result[key] = String(value);
17934
- }
17935
- return result;
17936
- }
17937
18308
 
17938
- // src/packing/resolver.ts
17939
- var NamespaceResolutionError = class extends Error {
17940
- constructor(itemType, itemName, namespace, message) {
17941
- super(message);
17942
- this.itemType = itemType;
17943
- this.itemName = itemName;
17944
- this.namespace = namespace;
17945
- this.name = "NamespaceResolutionError";
17946
- }
17947
- };
17948
- function parseQualifiedName(name) {
17949
- if (name.startsWith("@")) {
17950
- const firstSlash = name.indexOf("/");
17951
- if (firstSlash === -1) {
17952
- return [null, name];
17953
- }
17954
- const secondSlash = name.indexOf("/", firstSlash + 1);
17955
- if (secondSlash === -1) {
17956
- return [null, name];
17957
- }
17958
- return [name.substring(0, secondSlash), name.substring(secondSlash + 1)];
17959
- }
17960
- const separatorIndex = name.indexOf("/");
17961
- if (separatorIndex === -1) {
17962
- return [null, name];
17963
- }
17964
- return [name.substring(0, separatorIndex), name.substring(separatorIndex + 1)];
17965
- }
17966
- function qualifyName(packageId, shortName) {
17967
- return `${packageId}/${shortName}`;
17968
- }
17969
- function getShortName(name) {
17970
- const [, shortName] = parseQualifiedName(name);
17971
- return shortName;
17972
- }
17973
- function resolveTool(registry, name, namespace) {
17974
- const [pkgId, shortName] = parseQualifiedName(name);
17975
- if (pkgId !== null) {
17976
- const pkg = registry.packages[pkgId];
17977
- if (!pkg) {
17978
- throw new NamespaceResolutionError(
17979
- "tool",
17980
- name,
17981
- namespace,
17982
- `Package "${pkgId}" not found`
17983
- );
17984
- }
17985
- throw new NamespaceResolutionError(
17986
- "tool",
17987
- name,
17988
- namespace,
17989
- `Cannot access tool "${shortName}" from package "${pkgId}". Only entry point agents are accessible cross-package.`
17990
- );
17991
- }
17992
- if (namespace.type === "packed") {
17993
- const pkg = registry.packages[namespace.packageId];
17994
- if (!pkg) {
17995
- throw new NamespaceResolutionError(
17996
- "tool",
17997
- name,
17998
- namespace,
17999
- `Package "${namespace.packageId}" not found`
18000
- );
18001
- }
18002
- const loader2 = pkg.tools[shortName];
18003
- if (!loader2) {
18004
- throw new NamespaceResolutionError(
18005
- "tool",
18006
- name,
18007
- namespace,
18008
- `Tool "${shortName}" not found in package "${namespace.packageId}"`
18009
- );
18010
- }
18011
- return loader2;
18012
- }
18013
- const loader = registry.global.tools[shortName];
18014
- if (!loader) {
18015
- throw new NamespaceResolutionError(
18016
- "tool",
18017
- name,
18018
- namespace,
18019
- `Tool "${shortName}" not found in global namespace`
18020
- );
18021
- }
18022
- return loader;
18023
- }
18024
- function resolvePrompt(registry, name, namespace) {
18025
- const [pkgId, shortName] = parseQualifiedName(name);
18026
- if (pkgId !== null) {
18027
- throw new NamespaceResolutionError(
18028
- "prompt",
18029
- name,
18030
- namespace,
18031
- `Cannot access prompt "${shortName}" from package "${pkgId}". Prompts are package-internal.`
18032
- );
18033
- }
18034
- if (namespace.type === "packed") {
18035
- const pkg = registry.packages[namespace.packageId];
18036
- if (!pkg) {
18037
- throw new NamespaceResolutionError(
18038
- "prompt",
18039
- name,
18040
- namespace,
18041
- `Package "${namespace.packageId}" not found`
18042
- );
18043
- }
18044
- const loader2 = pkg.prompts[shortName];
18045
- if (!loader2) {
18046
- throw new NamespaceResolutionError(
18047
- "prompt",
18048
- name,
18049
- namespace,
18050
- `Prompt "${shortName}" not found in package "${namespace.packageId}"`
18051
- );
18052
- }
18053
- return loader2;
18054
- }
18055
- const loader = registry.global.prompts[shortName];
18056
- if (!loader) {
18057
- throw new NamespaceResolutionError(
18058
- "prompt",
18059
- name,
18060
- namespace,
18061
- `Prompt "${shortName}" not found in global namespace`
18309
+ // src/durable-objects/agentbuilder-migrations/0013_add_thread_activity.ts
18310
+ var migration49 = {
18311
+ version: 13,
18312
+ async up(sql) {
18313
+ sql.exec(`ALTER TABLE threads ADD COLUMN last_activity_at INTEGER`);
18314
+ sql.exec(`ALTER TABLE threads ADD COLUMN executing_since INTEGER`);
18315
+ sql.exec(
18316
+ `CREATE INDEX IF NOT EXISTS idx_threads_last_activity_at ON threads(last_activity_at)`
18062
18317
  );
18318
+ sql.exec(`UPDATE threads SET last_activity_at = created_at`);
18319
+ sql.exec(`
18320
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '13')
18321
+ `);
18063
18322
  }
18064
- return loader;
18065
- }
18066
- function resolveModel(registry, name, namespace) {
18067
- const [possiblePkgId, shortName] = parseQualifiedName(name);
18068
- if (possiblePkgId !== null && registry.packages[possiblePkgId]) {
18069
- throw new NamespaceResolutionError(
18070
- "model",
18071
- name,
18072
- namespace,
18073
- `Cannot use qualified model name "${name}". Models must be unqualified. If this is a provider model name, ensure no package named "${possiblePkgId}" exists.`
18323
+ };
18324
+
18325
+ // src/durable-objects/agentbuilder-migrations/0014_thread_activity_scale.ts
18326
+ var migration50 = {
18327
+ version: 14,
18328
+ async up(sql) {
18329
+ sql.exec(`ALTER TABLE threads ADD COLUMN last_message_preview TEXT`);
18330
+ sql.exec(
18331
+ `CREATE INDEX IF NOT EXISTS idx_threads_executing_since
18332
+ ON threads(executing_since) WHERE executing_since IS NOT NULL`
18333
+ );
18334
+ sql.exec(
18335
+ `UPDATE threads SET last_activity_at = created_at WHERE last_activity_at IS NULL`
18074
18336
  );
18337
+ sql.exec(`
18338
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '14')
18339
+ `);
18075
18340
  }
18076
- const lookupName = name;
18077
- if (namespace.type === "packed") {
18078
- const pkg = registry.packages[namespace.packageId];
18079
- if (pkg) {
18080
- const loader2 = pkg.models[lookupName];
18081
- if (loader2) {
18082
- return loader2;
18083
- }
18084
- }
18085
- const globalLoader = registry.global.models[lookupName];
18086
- if (globalLoader) {
18087
- return globalLoader;
18088
- }
18089
- throw new NamespaceResolutionError(
18090
- "model",
18091
- name,
18092
- namespace,
18093
- `Model "${lookupName}" not found in package "${namespace.packageId}" or global namespace`
18341
+ };
18342
+
18343
+ // src/durable-objects/agentbuilder-migrations/0015_clear_envelope_previews.ts
18344
+ var migration51 = {
18345
+ version: 15,
18346
+ async up(sql) {
18347
+ sql.exec(
18348
+ `UPDATE threads SET last_message_preview = NULL
18349
+ WHERE last_message_preview LIKE 'Subagent (reference: %'`
18094
18350
  );
18351
+ sql.exec(`
18352
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '15')
18353
+ `);
18095
18354
  }
18096
- const loader = registry.global.models[lookupName];
18097
- if (!loader) {
18098
- throw new NamespaceResolutionError(
18099
- "model",
18100
- name,
18101
- namespace,
18102
- `Model "${lookupName}" not found in global namespace`
18355
+ };
18356
+
18357
+ // src/durable-objects/agentbuilder-migrations/0016_add_origin_agent.ts
18358
+ var migration52 = {
18359
+ version: 16,
18360
+ async up(sql) {
18361
+ sql.exec(`ALTER TABLE threads ADD COLUMN origin_agent_name TEXT`);
18362
+ sql.exec(
18363
+ `CREATE INDEX IF NOT EXISTS idx_threads_origin_agent_name ON threads(origin_agent_name)`
18103
18364
  );
18365
+ sql.exec(`UPDATE threads SET origin_agent_name = agent_name`);
18366
+ sql.exec(`
18367
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '16')
18368
+ `);
18104
18369
  }
18105
- return loader;
18106
- }
18107
- function resolveAgent(registry, name, namespace) {
18108
- const [pkgId, shortName] = parseQualifiedName(name);
18109
- if (pkgId !== null) {
18110
- const pkg = registry.packages[pkgId];
18111
- if (!pkg) {
18112
- throw new NamespaceResolutionError(
18113
- "agent",
18114
- name,
18115
- namespace,
18116
- `Package "${pkgId}" not found`
18117
- );
18118
- }
18119
- if (!pkg.entryAgents.includes(shortName)) {
18120
- throw new NamespaceResolutionError(
18121
- "agent",
18122
- name,
18123
- namespace,
18124
- `Agent "${shortName}" is not an entry point in package "${pkgId}"`
18125
- );
18126
- }
18127
- const loader = pkg.agents[shortName];
18128
- if (!loader) {
18129
- throw new NamespaceResolutionError(
18130
- "agent",
18131
- name,
18132
- namespace,
18133
- `Agent "${shortName}" not found in package "${pkgId}"`
18134
- );
18135
- }
18136
- return loader;
18370
+ };
18371
+
18372
+ // src/durable-objects/agentbuilder-migrations/0017_add_thread_message_recency.ts
18373
+ var migration53 = {
18374
+ version: 17,
18375
+ async up(sql) {
18376
+ sql.exec(`ALTER TABLE threads ADD COLUMN last_message_at INTEGER`);
18377
+ sql.exec(`UPDATE threads SET last_message_at = created_at`);
18378
+ sql.exec(`
18379
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '17')
18380
+ `);
18137
18381
  }
18138
- if (namespace.type === "packed") {
18139
- const pkg = registry.packages[namespace.packageId];
18140
- if (!pkg) {
18141
- throw new NamespaceResolutionError(
18142
- "agent",
18143
- name,
18144
- namespace,
18145
- `Package "${namespace.packageId}" not found`
18146
- );
18147
- }
18148
- const loader = pkg.agents[shortName];
18149
- if (!loader) {
18150
- throw new NamespaceResolutionError(
18151
- "agent",
18152
- name,
18153
- namespace,
18154
- `Agent "${shortName}" not found in package "${namespace.packageId}"`
18155
- );
18156
- }
18157
- return loader;
18382
+ };
18383
+
18384
+ // src/durable-objects/agentbuilder-migrations/0018_auth_security.ts
18385
+ var migration54 = {
18386
+ version: 18,
18387
+ async up(sql) {
18388
+ sql.exec(`
18389
+ CREATE TABLE IF NOT EXISTS super_admin_sessions (
18390
+ token_hash TEXT PRIMARY KEY,
18391
+ created_at INTEGER NOT NULL,
18392
+ expires_at INTEGER NOT NULL
18393
+ )
18394
+ `);
18395
+ sql.exec(`
18396
+ CREATE INDEX IF NOT EXISTS idx_super_admin_sessions_expires
18397
+ ON super_admin_sessions(expires_at)
18398
+ `);
18399
+ sql.exec(`
18400
+ CREATE TABLE IF NOT EXISTS auth_rate_limits (
18401
+ bucket_key TEXT PRIMARY KEY,
18402
+ attempts INTEGER NOT NULL,
18403
+ reset_at INTEGER NOT NULL
18404
+ )
18405
+ `);
18406
+ sql.exec(`
18407
+ CREATE INDEX IF NOT EXISTS idx_auth_rate_limits_reset
18408
+ ON auth_rate_limits(reset_at)
18409
+ `);
18410
+ sql.exec(`
18411
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '18')
18412
+ `);
18158
18413
  }
18159
- const globalLoader = registry.global.agents[shortName];
18160
- if (globalLoader) {
18161
- return globalLoader;
18414
+ };
18415
+
18416
+ // src/durable-objects/agentbuilder-migrations/0019_scope_skills.ts
18417
+ var migration55 = {
18418
+ version: 19,
18419
+ async up(sql) {
18420
+ sql.exec(`
18421
+ CREATE TABLE skills_0019 (
18422
+ owner_id TEXT NOT NULL,
18423
+ name TEXT NOT NULL,
18424
+ description TEXT NOT NULL,
18425
+ version TEXT,
18426
+ enabled INTEGER NOT NULL DEFAULT 1,
18427
+ shared INTEGER NOT NULL DEFAULT 0,
18428
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
18429
+ updated_at INTEGER DEFAULT (strftime('%s', 'now')),
18430
+ PRIMARY KEY (owner_id, name)
18431
+ )
18432
+ `);
18433
+ sql.exec(`
18434
+ INSERT INTO skills_0019 (
18435
+ owner_id, name, description, version, enabled, shared, created_at, updated_at
18436
+ )
18437
+ SELECT '__instance_shared__', name, description, version, enabled, 1, created_at, updated_at
18438
+ FROM skills
18439
+ `);
18440
+ sql.exec(`
18441
+ CREATE TABLE skill_files_0019 (
18442
+ owner_id TEXT NOT NULL,
18443
+ skill_name TEXT NOT NULL,
18444
+ path TEXT NOT NULL,
18445
+ content TEXT NOT NULL,
18446
+ size INTEGER NOT NULL,
18447
+ PRIMARY KEY (owner_id, skill_name, path),
18448
+ FOREIGN KEY (owner_id, skill_name) REFERENCES skills_0019(owner_id, name) ON DELETE CASCADE
18449
+ )
18450
+ `);
18451
+ sql.exec(`
18452
+ INSERT INTO skill_files_0019 (owner_id, skill_name, path, content, size)
18453
+ SELECT '__instance_shared__', skill_name, path, content, size FROM skill_files
18454
+ `);
18455
+ sql.exec(`DROP TABLE skill_files`);
18456
+ sql.exec(`DROP TABLE skills`);
18457
+ sql.exec(`ALTER TABLE skills_0019 RENAME TO skills`);
18458
+ sql.exec(`ALTER TABLE skill_files_0019 RENAME TO skill_files`);
18459
+ sql.exec(`CREATE INDEX idx_skills_owner_enabled ON skills(owner_id, enabled)`);
18460
+ sql.exec(`CREATE INDEX idx_skills_shared_enabled ON skills(shared, enabled)`);
18461
+ sql.exec(`CREATE INDEX idx_skill_files_skill ON skill_files(owner_id, skill_name)`);
18462
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '19')`);
18162
18463
  }
18163
- for (const [pkgId2, pkg] of Object.entries(registry.packages)) {
18164
- if (pkg.entryAgents.includes(shortName)) {
18165
- const loader = pkg.agents[shortName];
18166
- if (loader) {
18167
- return loader;
18168
- }
18169
- }
18464
+ };
18465
+
18466
+ // src/durable-objects/agentbuilder-migrations/0020_websocket_tickets.ts
18467
+ var migration56 = {
18468
+ version: 20,
18469
+ async up(sql) {
18470
+ sql.exec(`
18471
+ CREATE TABLE websocket_tickets (
18472
+ ticket_hash TEXT PRIMARY KEY,
18473
+ pathname TEXT NOT NULL,
18474
+ auth_json TEXT NOT NULL,
18475
+ created_at INTEGER NOT NULL,
18476
+ expires_at INTEGER NOT NULL
18477
+ )
18478
+ `);
18479
+ sql.exec(`
18480
+ CREATE INDEX idx_websocket_tickets_expires
18481
+ ON websocket_tickets(expires_at)
18482
+ `);
18483
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '20')`);
18484
+ }
18485
+ };
18486
+
18487
+ // src/durable-objects/agentbuilder-migrations/0021_platform_handoff_jtis.ts
18488
+ var migration57 = {
18489
+ version: 21,
18490
+ async up(sql) {
18491
+ sql.exec(`
18492
+ CREATE TABLE platform_handoff_jtis (
18493
+ jti TEXT PRIMARY KEY,
18494
+ consumed_at INTEGER NOT NULL,
18495
+ expires_at INTEGER NOT NULL
18496
+ )
18497
+ `);
18498
+ sql.exec(`
18499
+ CREATE INDEX idx_platform_handoff_jtis_expires
18500
+ ON platform_handoff_jtis(expires_at)
18501
+ `);
18502
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '21')`);
18170
18503
  }
18171
- throw new NamespaceResolutionError(
18172
- "agent",
18173
- name,
18174
- namespace,
18175
- `Agent "${shortName}" not found in global namespace`
18176
- );
18177
- }
18178
- function resolveHook(registry, name, namespace) {
18179
- const [pkgId, shortName] = parseQualifiedName(name);
18180
- if (pkgId !== null) {
18181
- throw new NamespaceResolutionError(
18182
- "hook",
18183
- name,
18184
- namespace,
18185
- `Cannot use qualified hook name "${name}". Hooks must be unqualified.`
18504
+ };
18505
+
18506
+ // src/durable-objects/agentbuilder-migrations/index.ts
18507
+ var migrations2 = [migration37, migration38, migration39, migration40, migration41, migration42, migration43, migration44, migration45, migration46, migration47, migration48, migration49, migration50, migration51, migration52, migration53, migration54, migration55, migration56, migration57];
18508
+ var LATEST_SCHEMA_VERSION2 = migrations2.reduce(
18509
+ (max, migration58) => Math.max(max, migration58.version),
18510
+ 0
18511
+ );
18512
+
18513
+ // src/utils/crypto.ts
18514
+ var CryptoUtil = class {
18515
+ static algorithm = "aes-256-gcm";
18516
+ /**
18517
+ * Encrypt a string value
18518
+ */
18519
+ static async encrypt(text, key) {
18520
+ const encoder = new TextEncoder();
18521
+ const data = encoder.encode(text);
18522
+ const cryptoKey = await crypto.subtle.importKey(
18523
+ "raw",
18524
+ hexToBuffer(key),
18525
+ { name: "AES-GCM" },
18526
+ false,
18527
+ ["encrypt"]
18528
+ );
18529
+ const iv = crypto.getRandomValues(new Uint8Array(12));
18530
+ const encrypted = await crypto.subtle.encrypt(
18531
+ {
18532
+ name: "AES-GCM",
18533
+ iv
18534
+ },
18535
+ cryptoKey,
18536
+ data
18186
18537
  );
18538
+ const combined = new Uint8Array(iv.length + encrypted.byteLength);
18539
+ combined.set(iv);
18540
+ combined.set(new Uint8Array(encrypted), iv.length);
18541
+ return bufferToBase64(combined);
18187
18542
  }
18188
- if (namespace.type === "packed") {
18189
- const pkg = registry.packages[namespace.packageId];
18190
- if (pkg) {
18191
- const loader2 = pkg.hooks[shortName];
18192
- if (loader2) {
18193
- return loader2;
18194
- }
18195
- }
18196
- return null;
18543
+ /**
18544
+ * Decrypt a string value
18545
+ */
18546
+ static async decrypt(encryptedText, key) {
18547
+ const combined = base64ToBuffer2(encryptedText);
18548
+ const iv = combined.slice(0, 12);
18549
+ const encrypted = combined.slice(12);
18550
+ const cryptoKey = await crypto.subtle.importKey(
18551
+ "raw",
18552
+ hexToBuffer(key),
18553
+ { name: "AES-GCM" },
18554
+ false,
18555
+ ["decrypt"]
18556
+ );
18557
+ const decrypted = await crypto.subtle.decrypt(
18558
+ {
18559
+ name: "AES-GCM",
18560
+ iv
18561
+ },
18562
+ cryptoKey,
18563
+ encrypted
18564
+ );
18565
+ const decoder = new TextDecoder();
18566
+ return decoder.decode(decrypted);
18197
18567
  }
18198
- const loader = registry.global.hooks[shortName];
18199
- return loader || null;
18200
- }
18201
- function getVisibleToolNames(registry, namespace) {
18202
- if (namespace.type === "packed") {
18203
- const pkg = registry.packages[namespace.packageId];
18204
- return pkg ? Object.keys(pkg.tools) : [];
18568
+ };
18569
+ function hexToBuffer(hex) {
18570
+ const bytes = new Uint8Array(hex.length / 2);
18571
+ for (let i = 0; i < hex.length; i += 2) {
18572
+ bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
18205
18573
  }
18206
- return Object.keys(registry.global.tools);
18574
+ return bytes;
18207
18575
  }
18208
- function getVisibleAgentNames(registry, namespace) {
18209
- if (namespace.type === "packed") {
18210
- const pkg = registry.packages[namespace.packageId];
18211
- const ownAgents = pkg ? Object.keys(pkg.agents) : [];
18212
- const otherEntryPoints = Object.entries(registry.packages).filter(([id]) => id !== namespace.packageId).flatMap(
18213
- ([pkgId, pkg2]) => pkg2.entryAgents.map((name) => qualifyName(pkgId, name))
18214
- );
18215
- return [...ownAgents, ...otherEntryPoints];
18576
+ function bufferToBase64(buffer) {
18577
+ let binary = "";
18578
+ for (let i = 0; i < buffer.byteLength; i++) {
18579
+ binary += String.fromCharCode(buffer[i]);
18216
18580
  }
18217
- const globalAgents = Object.keys(registry.global.agents);
18218
- const entryPoints = Object.values(registry.packages).flatMap((pkg) => pkg.entryAgents);
18219
- return [.../* @__PURE__ */ new Set([...globalAgents, ...entryPoints])];
18581
+ return btoa(binary);
18220
18582
  }
18221
- function getVisiblePromptNames(registry, namespace) {
18222
- if (namespace.type === "packed") {
18223
- const pkg = registry.packages[namespace.packageId];
18224
- return pkg ? Object.keys(pkg.prompts) : [];
18583
+ function base64ToBuffer2(base64) {
18584
+ const binary = atob(base64);
18585
+ const bytes = new Uint8Array(binary.length);
18586
+ for (let i = 0; i < binary.length; i++) {
18587
+ bytes[i] = binary.charCodeAt(i);
18225
18588
  }
18226
- return Object.keys(registry.global.prompts);
18589
+ return bytes;
18227
18590
  }
18228
- function getVisibleModelNames(registry, namespace) {
18229
- if (namespace.type === "packed") {
18230
- const pkg = registry.packages[namespace.packageId];
18231
- const ownModels = pkg ? Object.keys(pkg.models) : [];
18232
- const globalModels = Object.keys(registry.global.models);
18233
- return [.../* @__PURE__ */ new Set([...ownModels, ...globalModels])];
18234
- }
18235
- return Object.keys(registry.global.models);
18591
+ async function encryptEnvs(env, encryptionKey) {
18592
+ const plaintext = JSON.stringify(env);
18593
+ return CryptoUtil.encrypt(plaintext, encryptionKey);
18236
18594
  }
18237
- function createNamespaceContext(agentDef) {
18238
- if (agentDef?.__package) {
18239
- return { type: "packed", packageId: agentDef.__package.packageId };
18595
+ async function decryptEnvs(encrypted, encryptionKey) {
18596
+ const plaintext = await CryptoUtil.decrypt(encrypted, encryptionKey);
18597
+ const parsed = JSON.parse(plaintext);
18598
+ if (!parsed || typeof parsed !== "object") {
18599
+ return {};
18240
18600
  }
18241
- return { type: "global" };
18242
- }
18243
- function isQualifiedName(name) {
18244
- if (name.startsWith("@")) {
18245
- const firstSlash = name.indexOf("/");
18246
- if (firstSlash === -1) return false;
18247
- return name.indexOf("/", firstSlash + 1) !== -1;
18601
+ const result = {};
18602
+ for (const [key, value] of Object.entries(parsed)) {
18603
+ if (value === void 0 || value === null) continue;
18604
+ result[key] = String(value);
18248
18605
  }
18249
- return name.includes("/");
18606
+ return result;
18250
18607
  }
18251
18608
 
18252
18609
  // src/durable-objects/DurableAgentBuilder.ts
18610
+ init_resolver();
18253
18611
  init_types();
18254
18612
 
18255
18613
  // src/skills/frontmatter.ts
@@ -18341,6 +18699,7 @@ function validateSkillFiles(files) {
18341
18699
 
18342
18700
  // src/durable-objects/DurableAgentBuilder.ts
18343
18701
  init_scope();
18702
+ init_ParentHookResolver();
18344
18703
  var ENV_TYPES_PROPERTY = "__agentbuilder_env_types";
18345
18704
  function parseStoredPermissions(value) {
18346
18705
  if (typeof value !== "string" || !value.trim()) return null;
@@ -19022,16 +19381,29 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
19022
19381
  } catch {
19023
19382
  return;
19024
19383
  }
19025
- const hookIds = Array.isArray(agentDef?.hooks) ? agentDef.hooks : [];
19026
- if (hookIds.length === 0) {
19027
- return;
19028
- }
19029
- let hooksRegistry;
19030
- try {
19031
- hooksRegistry = this.hooks();
19032
- } catch {
19033
- return;
19034
- }
19384
+ const packageId = agentDef?.__package?.packageId;
19385
+ const rawPromptName = agentDef?.sideA?.prompt;
19386
+ const promptName = rawPromptName && packageId && !isQualifiedName(rawPromptName) ? `${packageId}/${rawPromptName}` : rawPromptName;
19387
+ const promptDef = promptName ? await this.loadPrompt(promptName) : void 0;
19388
+ const bindings = await resolveHooksForPrompt(
19389
+ {
19390
+ host: this,
19391
+ agentName: thread.agent_name,
19392
+ agent: agentDef,
19393
+ promptName,
19394
+ prompt: promptDef,
19395
+ isOptionalRelationshipEnabled: async (envName) => {
19396
+ const value = await this.resolveThreadEnv({
19397
+ threadId: thread.id,
19398
+ property: envName,
19399
+ promptName: promptName ?? agentDef.name ?? thread.agent_name
19400
+ });
19401
+ return ["true", "1", "yes"].includes((value ?? "").trim().toLowerCase());
19402
+ }
19403
+ },
19404
+ "after_thread_created"
19405
+ );
19406
+ if (bindings.length === 0) return;
19035
19407
  const durableId = this.env.AGENT_BUILDER_THREAD.idFromName(thread.id);
19036
19408
  const threadStub = this.env.AGENT_BUILDER_THREAD.get(durableId);
19037
19409
  const { ThreadStateImpl: ThreadStateImpl2 } = await Promise.resolve().then(() => (init_ThreadStateImpl(), ThreadStateImpl_exports));
@@ -19046,16 +19418,14 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
19046
19418
  terminated: thread.terminated,
19047
19419
  created_at: thread.created_at
19048
19420
  });
19049
- for (const hookId of hookIds) {
19050
- const hookLoader = hooksRegistry[hookId];
19051
- if (!hookLoader) continue;
19421
+ for (const binding of bindings) {
19052
19422
  try {
19053
- const hookDef = await hookLoader();
19054
- if (hookDef?.hook === "after_thread_created") {
19055
- await hookDef.execute(threadState);
19056
- }
19423
+ await binding.execute(threadState, binding.context);
19057
19424
  } catch (error) {
19058
- console.error(`[Hooks] \u2717 Error running after_thread_created hook '${hookId}':`, error);
19425
+ console.error(
19426
+ `[Hooks] \u2717 Error running after_thread_created hook '${binding.id}':`,
19427
+ error
19428
+ );
19059
19429
  }
19060
19430
  }
19061
19431
  }
@@ -20267,6 +20637,15 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20267
20637
  * Load a prompt definition by name.
20268
20638
  */
20269
20639
  async loadPrompt(name) {
20640
+ if (isQualifiedName(name)) {
20641
+ const [packageId, promptName] = parseQualifiedName(name);
20642
+ const pkg = packageId ? this.registry()?.packages?.[packageId] : void 0;
20643
+ const loader2 = pkg?.prompts?.[promptName];
20644
+ if (!loader2) {
20645
+ throw new Error(`Prompt not found: ${name}`);
20646
+ }
20647
+ return await loader2();
20648
+ }
20270
20649
  const registry = this.prompts();
20271
20650
  const loader = registry[name];
20272
20651
  if (!loader) {
@@ -21555,6 +21934,8 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
21555
21934
  // src/durable-objects/DurableThread.ts
21556
21935
  init_StreamManager();
21557
21936
  init_LLMRequest();
21937
+ init_ThreadStateImpl();
21938
+ init_resolver();
21558
21939
 
21559
21940
  // src/durable-objects/logDetailStream.ts
21560
21941
  var LOG_DETAIL_HEAVY_FIELDS = [
@@ -22228,6 +22609,7 @@ var DurableThread = class _DurableThread extends DurableObject {
22228
22609
  type: agentDef.type,
22229
22610
  created_at: Date.now(),
22230
22611
  max_session_turns: agentDef.maxSessionTurns,
22612
+ _package: agentDef.__package,
22231
22613
  side_a_label: agentDef.sideA?.label,
22232
22614
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
22233
22615
  side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
@@ -22257,7 +22639,8 @@ var DurableThread = class _DurableThread extends DurableObject {
22257
22639
  side_b_session_fail_attachments_property: sideBSession.fail.attachmentsProperty,
22258
22640
  side_b_session_status_tool: sideBSession.status.toolName,
22259
22641
  side_b_session_status_message_property: sideBSession.status.messageProperty,
22260
- side_b_session_status_attachments_property: sideBSession.status.attachmentsProperty
22642
+ side_b_session_status_attachments_property: sideBSession.status.attachmentsProperty,
22643
+ hooks: Array.isArray(agentDef.hooks) ? agentDef.hooks : void 0
22261
22644
  };
22262
22645
  }
22263
22646
  /**
@@ -22927,6 +23310,29 @@ var DurableThread = class _DurableThread extends DurableObject {
22927
23310
  }
22928
23311
  return projections;
22929
23312
  }
23313
+ /**
23314
+ * Build hook resolution context and a genuine at-rest ThreadState.
23315
+ * Resolution failures abort insertion so lifecycle hooks cannot be bypassed.
23316
+ */
23317
+ async buildAtRestHookState(threadId) {
23318
+ try {
23319
+ const metadata = await this.getThreadMetadata(threadId);
23320
+ const agentConfig = await this.loadAgentConfig(metadata.agent_id);
23321
+ const threadInstance = this;
23322
+ return {
23323
+ hookState: {
23324
+ agentConfig,
23325
+ thread: { instance: threadInstance, metadata }
23326
+ },
23327
+ threadState: ThreadStateImpl.fromThreadInstance(threadInstance, metadata)
23328
+ };
23329
+ } catch (error) {
23330
+ throw new Error(
23331
+ `Unable to resolve create-message hooks for ${threadId}`,
23332
+ { cause: error }
23333
+ );
23334
+ }
23335
+ }
22930
23336
  /**
22931
23337
  * Inject a top-level message without starting a new execution.
22932
23338
  *
@@ -22939,22 +23345,7 @@ var DurableThread = class _DurableThread extends DurableObject {
22939
23345
  const id = crypto.randomUUID();
22940
23346
  const createdAt = Date.now() * TIMESTAMP_MULTIPLIER;
22941
23347
  const silent = message.silent === true;
22942
- const metadataJson = message.metadata ? JSON.stringify(message.metadata) : null;
22943
- await this.ctx.storage.sql.exec(
22944
- `INSERT INTO messages (id, role, content, created_at, status, silent, parent_id, depth, metadata, prompt)
22945
- VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)`,
22946
- id,
22947
- message.role,
22948
- message.content,
22949
- createdAt,
22950
- "completed",
22951
- silent ? 1 : 0,
22952
- null,
22953
- 0,
22954
- metadataJson,
22955
- null
22956
- );
22957
- const inserted = {
23348
+ let inserted = {
22958
23349
  id,
22959
23350
  role: message.role,
22960
23351
  content: message.content,
@@ -22965,9 +23356,37 @@ var DurableThread = class _DurableThread extends DurableObject {
22965
23356
  depth: 0,
22966
23357
  metadata: message.metadata
22967
23358
  };
23359
+ const { hookState, threadState } = await this.buildAtRestHookState(threadId);
23360
+ const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
23361
+ inserted = await FlowEngine2.runBeforeCreateMessageHook(
23362
+ hookState,
23363
+ inserted,
23364
+ threadState,
23365
+ { propagateHookErrors: true }
23366
+ );
23367
+ const metadataJson = inserted.metadata ? JSON.stringify(inserted.metadata) : null;
23368
+ await this.ctx.storage.sql.exec(
23369
+ `INSERT INTO messages (id, role, content, created_at, status, silent, parent_id, depth, metadata, prompt)
23370
+ VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)`,
23371
+ inserted.id,
23372
+ inserted.role,
23373
+ inserted.content,
23374
+ inserted.created_at,
23375
+ inserted.status ?? "completed",
23376
+ inserted.silent ? 1 : 0,
23377
+ inserted.parent_id ?? null,
23378
+ inserted.depth ?? 0,
23379
+ metadataJson,
23380
+ null
23381
+ );
23382
+ await FlowEngine2.runAfterCreateMessageHook(
23383
+ hookState,
23384
+ inserted,
23385
+ threadState
23386
+ );
22968
23387
  this.broadcastMessage(inserted);
22969
23388
  const record = inserted;
22970
- const visibleUserInjection = message.role === "user" && !silent && typeof message.content === "string" && message.content.trim().length > 0 && !isRuntimeSubagentEnvelope(message.content);
23389
+ const visibleUserInjection = inserted.role === "user" && !inserted.silent && typeof inserted.content === "string" && inserted.content.trim().length > 0 && !isRuntimeSubagentEnvelope(inserted.content);
22971
23390
  if (visibleUserInjection || isVisibleTerminalAssistantMessage(record)) {
22972
23391
  const stamp = this.recordRegistryActivity(
22973
23392
  threadId,
@@ -27425,6 +27844,8 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
27425
27844
  type: agentDef.type,
27426
27845
  created_at: Date.now(),
27427
27846
  max_session_turns: agentDef.maxSessionTurns,
27847
+ hooks: agentDef.hooks ?? [],
27848
+ _package: agentDef.__package,
27428
27849
  side_a_label: agentDef.sideA?.label,
27429
27850
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
27430
27851
  side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
@@ -27650,6 +28071,8 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
27650
28071
  type: agentDef.type,
27651
28072
  created_at: Date.now(),
27652
28073
  max_session_turns: agentDef.maxSessionTurns,
28074
+ hooks: agentDef.hooks ?? [],
28075
+ _package: agentDef.__package,
27653
28076
  side_a_label: agentDef.sideA?.label,
27654
28077
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
27655
28078
  side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
@@ -27806,6 +28229,8 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
27806
28229
  type: agentDef.type,
27807
28230
  created_at: Date.now(),
27808
28231
  max_session_turns: agentDef.maxSessionTurns,
28232
+ hooks: agentDef.hooks ?? [],
28233
+ _package: agentDef.__package,
27809
28234
  side_a_label: agentDef.sideA?.label,
27810
28235
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
27811
28236
  side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
@@ -29462,8 +29887,7 @@ var DurableUserHub = class extends DurableObject {
29462
29887
  init_utilities();
29463
29888
 
29464
29889
  // src/agents/FlowStateSdk.ts
29465
- init_types();
29466
- var FlowStateSdk = class _FlowStateSdk {
29890
+ var FlowStateSdk = class {
29467
29891
  /**
29468
29892
  * Queue a new tool call to be executed in the current flow
29469
29893
  *
@@ -29530,63 +29954,8 @@ var FlowStateSdk = class _FlowStateSdk {
29530
29954
  * ```
29531
29955
  */
29532
29956
  static async injectMessage(flow, options) {
29533
- const messageId = options.id ?? crypto.randomUUID();
29534
- const timestamp = Date.now() * TIMESTAMP_MULTIPLIER;
29535
- const silent = options.silent ?? false;
29536
- const forceTopLevel = options.forceTopLevel ?? false;
29537
- const message = {
29538
- id: messageId,
29539
- role: options.role,
29540
- content: options.content,
29541
- name: options.name ?? null,
29542
- tool_call_id: options.toolCallId ?? null,
29543
- created_at: timestamp,
29544
- status: "completed",
29545
- silent,
29546
- parent_id: forceTopLevel ? null : flow.parentMessageId ?? null,
29547
- depth: forceTopLevel ? 0 : flow.depth
29548
- };
29549
- if (options.beforeMessageId) {
29550
- const cursor = await flow.storage.sql.exec(
29551
- `SELECT created_at FROM messages WHERE id = ?`,
29552
- options.beforeMessageId
29553
- );
29554
- const rows = cursor.toArray();
29555
- const targetMessage = rows[0];
29556
- if (!targetMessage) {
29557
- throw new Error(
29558
- `Message with id ${options.beforeMessageId} not found`
29559
- );
29560
- }
29561
- message.created_at = targetMessage.created_at - 1;
29562
- }
29563
- const currentPrompt = flow.promptPath.length > 0 ? flow.promptPath[flow.promptPath.length - 1] : null;
29564
- await flow.storage.sql.exec(
29565
- `INSERT INTO messages (id, role, content, name, tool_call_id, created_at, status, silent, parent_id, depth, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
29566
- messageId,
29567
- message.role,
29568
- message.content,
29569
- message.name,
29570
- message.tool_call_id,
29571
- message.created_at,
29572
- message.status,
29573
- silent ? 1 : 0,
29574
- message.parent_id,
29575
- message.depth,
29576
- currentPrompt
29577
- );
29578
- if (!silent) {
29579
- flow.messageHistory.push(message);
29580
- } else {
29581
- flow.messageHistory = await _FlowStateSdk.reloadHistory(flow);
29582
- }
29583
- if (flow.emitMessage) {
29584
- flow.emitMessage(message);
29585
- }
29586
- if (options.forceTurn) {
29587
- _FlowStateSdk.forceTurn(flow, options.forceTurn);
29588
- }
29589
- return message;
29957
+ const { injectMessage: injectMessage2 } = await Promise.resolve().then(() => (init_utilities(), utilities_exports));
29958
+ return injectMessage2(flow, options);
29590
29959
  }
29591
29960
  /**
29592
29961
  * Get messages from the thread history
@@ -29675,9 +30044,9 @@ var FlowStateSdk = class _FlowStateSdk {
29675
30044
  /**
29676
30045
  * Reload message history from storage
29677
30046
  *
29678
- * Re-loads the complete message history from the thread's SQLite storage,
29679
- * applying any filter_messages hooks. This is useful when you need to refresh
29680
- * the message history after making changes (e.g., injecting silent messages).
30047
+ * Re-loads the complete message history from the thread's SQLite storage.
30048
+ * This is useful when you need to refresh the message history after making
30049
+ * changes (e.g., injecting messages).
29681
30050
  *
29682
30051
  * @param flow - The current FlowState
29683
30052
  * @returns Array of messages (reloaded from storage)
@@ -29818,6 +30187,7 @@ function enhanceFlowState(flow) {
29818
30187
  init_context();
29819
30188
  init_ProviderRegistry();
29820
30189
  init_types2();
30190
+ init_resolver();
29821
30191
 
29822
30192
  export { CLIENT_STREAM_CHUNK_BYTES, CodeExecutionBridge, DurableAgentBuilder, DurableThread, DurableUserHub, FlowStateSdk, NamespaceResolutionError, ProviderRegistry, ThreadWatchdog, applyRuntimeCors, authenticate, buildImageDescription, callClient, callClientStream, canAccessThread, cat, createNamespaceContext, createThreadEndpointHandler, decodeStreamChunk, defineController2 as defineController, emitThreadEvent, encodeStreamChunk, endpointCallerFromAuth, enhanceFlowState, exists, find, forceTurn, generateImageDescription, getFileStats, getMessages, getMessagesToSummarize, getShortName, getThumbnail, getUnsummarizedImageAttachments, getVisibleAgentNames, getVisibleModelNames, getVisiblePromptNames, getVisibleToolNames, grep, hasImageAttachments, head, injectMessage, isAdminOnlyApiRoute, isHostedInstance, isPublicApiRoute, isQualifiedName, isSameOriginBrowserRequest, isTrustedLocalDevelopmentRequest, linkFile, 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 };
29823
30193
  //# sourceMappingURL=runtime.js.map