@promptbook/cli 0.113.0-5 → 0.113.0-6

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 (36) hide show
  1. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +13 -1
  2. package/apps/agents-server/src/search/createDefaultServerSearchProviders/loadLocalOrganizationSearchDataset.ts +12 -1
  3. package/apps/agents-server/src/utils/agentOrganization/loadAgentOrganizationState.ts +13 -1
  4. package/apps/agents-server/src/utils/createLocalAgentSourceImporter.ts +159 -0
  5. package/apps/agents-server/src/utils/createMissingImportedAgentFallback.ts +60 -0
  6. package/apps/agents-server/src/utils/customDomainRouting.ts +157 -12
  7. package/apps/agents-server/src/utils/externalChatRunner/processExternalUserChatJob.ts +1 -1
  8. package/apps/agents-server/src/utils/importAgentWithFallback.ts +1 -58
  9. package/apps/agents-server/src/utils/localAgentRouteReferences.ts +167 -0
  10. package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +1 -1
  11. package/apps/agents-server/src/utils/managementApi/managementApiAgents.ts +17 -3
  12. package/apps/agents-server/src/utils/resolveAgentStateFromSource.ts +7 -1
  13. package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +54 -5
  14. package/apps/agents-server/src/utils/resolveServerAgentContext.ts +12 -1
  15. package/apps/agents-server/src/utils/resolveStoredAgentState.ts +2 -1
  16. package/apps/agents-server/src/utils/userChat/userChatMessageLifecycle.ts +39 -9
  17. package/apps/agents-server/tests/e2e/support/ChatHistoryNavigationSupport.ts +5 -0
  18. package/esm/index.es.js +24 -36
  19. package/esm/index.es.js.map +1 -1
  20. package/esm/src/version.d.ts +1 -1
  21. package/package.json +1 -1
  22. package/src/avatars/avatarAnimationScheduler.ts +2 -1
  23. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +6 -2
  24. package/src/avatars/visuals/octopus3d4AvatarVisual.ts +14 -18
  25. package/src/cli/cli-commands/agents-server/buildAgentsServer.ts +3 -1
  26. package/src/cli/cli-commands/coder/boilerplateTemplates.ts +14 -21
  27. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +1 -4
  28. package/src/cli/cli-commands/coder/run.ts +2 -9
  29. package/src/commitments/TEAM/TEAM.ts +5 -5
  30. package/src/other/templates/getTemplatesPipelineCollection.ts +845 -662
  31. package/src/utils/ascii-art/convertImageDataToAsciiArt.ts +1 -4
  32. package/src/version.ts +2 -2
  33. package/src/versions.txt +1 -0
  34. package/umd/index.umd.js +24 -36
  35. package/umd/index.umd.js.map +1 -1
  36. package/umd/src/version.d.ts +1 -1
@@ -8,6 +8,7 @@ import {
8
8
  import { loadFederatedAgentImportConfiguration } from '@/src/utils/federatedAgentImportConfiguration';
9
9
  import { getCurrentUser } from '@/src/utils/getCurrentUser';
10
10
  import { getWellKnownAgentUrl } from '@/src/utils/getWellKnownAgentUrl';
11
+ import { createLocalAgentSourceImporter } from '@/src/utils/createLocalAgentSourceImporter';
11
12
  import { resolveInheritedAgentSource } from '@/src/utils/resolveInheritedAgentSource';
12
13
  import { padBook, validateBook } from '@promptbook-local/core';
13
14
  import type { string_agent_url } from '@promptbook-local/types';
@@ -96,13 +97,24 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
96
97
  const agentSource = resolvedAgentContext.unresolvedAgentSource;
97
98
  const agentReferenceResolver = resolvedAgentContext.scopedAgentReferenceResolver;
98
99
  const federatedAgentImportConfiguration = await loadFederatedAgentImportConfiguration();
100
+ const adamAgentUrl = await getWellKnownAgentUrl('ADAM');
101
+ const localServerUrl = new URL(request.url).origin;
102
+ const agentSourceImporter = createLocalAgentSourceImporter({
103
+ collection,
104
+ localServerUrls: [localServerUrl],
105
+ localAgentUrls: [adamAgentUrl],
106
+ adamAgentUrl,
107
+ fallbackResolver: baseAgentReferenceResolver,
108
+ federatedAgentImportConfiguration,
109
+ });
99
110
  const effectiveAgentSource = await resolveInheritedAgentSource(agentSource, {
100
- adamAgentUrl: await getWellKnownAgentUrl('ADAM'),
111
+ adamAgentUrl,
101
112
  recursionLevel,
102
113
  currentAgentUrl: resolvedAgentContext.canonicalAgentUrl,
103
114
  inheritancePath: inheritancePath as Array<string_agent_url>,
104
115
  agentReferenceResolver,
105
116
  federatedAgentImportConfiguration,
117
+ agentSourceImporter,
106
118
  });
107
119
  const etag = `W/"${computeHash(effectiveAgentSource)}"`;
108
120
 
@@ -2,7 +2,9 @@ import { $getTableName } from '../../database/$getTableName';
2
2
  import { $provideSupabaseForServer } from '../../database/$provideSupabaseForServer';
3
3
  import type { AgentsServerDatabase } from '../../database/schema';
4
4
  import type { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
5
+ import { $provideAgentCollectionForServer } from '../../tools/$provideAgentCollectionForServer';
5
6
  import { $provideAgentReferenceResolver } from '../../utils/agentReferenceResolver/$provideAgentReferenceResolver';
7
+ import { createLocalAgentSourceImporter } from '../../utils/createLocalAgentSourceImporter';
6
8
  import { getWellKnownAgentUrl } from '../../utils/getWellKnownAgentUrl';
7
9
  import { resolveCurrentOrInternalServerOrigin } from '../../utils/resolveCurrentOrInternalServerOrigin';
8
10
  import { resolveStoredAgentStates } from '../../utils/resolveStoredAgentState';
@@ -85,12 +87,21 @@ export async function loadLocalOrganizationSearchDataset(options: {
85
87
  const folders = (folderResult.data || []) as AgentFolderSearchRow[];
86
88
  const localServerUrl = await resolveCurrentOrInternalServerOrigin();
87
89
  const agentReferenceResolver = await $provideAgentReferenceResolver();
90
+ const adamAgentUrl = await getWellKnownAgentUrl('ADAM');
91
+ const agentSourceImporter = createLocalAgentSourceImporter({
92
+ collection: await $provideAgentCollectionForServer(),
93
+ localServerUrls: [localServerUrl],
94
+ localAgentUrls: [adamAgentUrl],
95
+ adamAgentUrl,
96
+ fallbackResolver: agentReferenceResolver,
97
+ });
88
98
  const resolvedAgents = await resolveStoredAgentStates(
89
99
  (agentResult.data || []) as Array<Pick<AgentSearchRow, 'id' | 'agentName' | 'permanentId' | 'agentSource' | 'folderId' | 'visibility'>>,
90
100
  {
91
101
  localServerUrl,
92
- adamAgentUrl: await getWellKnownAgentUrl('ADAM'),
102
+ adamAgentUrl,
93
103
  agentReferenceResolver,
104
+ agentSourceImporter,
94
105
  },
95
106
  );
96
107
  const agents = resolvedAgents.map((agent) => ({
@@ -2,7 +2,9 @@ import type { AgentBasicInformation } from '../../../../../src/book-2.0/agent-so
2
2
  import { $getTableName } from '../../database/$getTableName';
3
3
  import { $provideSupabaseForServer } from '../../database/$provideSupabaseForServer';
4
4
  import type { AgentsServerDatabase } from '../../database/schema';
5
+ import { $provideAgentCollectionForServer } from '../../tools/$provideAgentCollectionForServer';
5
6
  import { $provideAgentReferenceResolver } from '../agentReferenceResolver/$provideAgentReferenceResolver';
7
+ import { createLocalAgentSourceImporter } from '../createLocalAgentSourceImporter';
6
8
  import { getCurrentUser } from '../getCurrentUser';
7
9
  import { getWellKnownAgentUrl } from '../getWellKnownAgentUrl';
8
10
  import { resolveCurrentOrInternalServerOrigin } from '../resolveCurrentOrInternalServerOrigin';
@@ -187,14 +189,24 @@ async function loadResolvedOrganizationSnapshot(options: {
187
189
 
188
190
  const localServerUrl = await resolveCurrentOrInternalServerOrigin();
189
191
  const agentReferenceResolver = await $provideAgentReferenceResolver();
192
+ const adamAgentUrl = await getWellKnownAgentUrl('ADAM');
193
+ const collection = await $provideAgentCollectionForServer();
194
+ const agentSourceImporter = createLocalAgentSourceImporter({
195
+ collection,
196
+ localServerUrls: [localServerUrl],
197
+ localAgentUrls: [adamAgentUrl],
198
+ adamAgentUrl,
199
+ fallbackResolver: agentReferenceResolver,
200
+ });
190
201
  const resolvedAgents = await resolveStoredAgentStates(
191
202
  (agentResult.data || []) as Array<
192
203
  Pick<AgentRow, 'agentName' | 'agentSource' | 'permanentId' | 'visibility' | 'folderId' | 'sortOrder'>
193
204
  >,
194
205
  {
195
206
  localServerUrl,
196
- adamAgentUrl: await getWellKnownAgentUrl('ADAM'),
207
+ adamAgentUrl,
197
208
  agentReferenceResolver,
209
+ agentSourceImporter,
198
210
  },
199
211
  );
200
212
  const agents = resolvedAgents.map((agent) =>
@@ -0,0 +1,159 @@
1
+ import type { AgentReferenceResolver } from '../../../../src/book-2.0/agent-source/AgentReferenceResolver';
2
+ import type { AgentCollection } from '../../../../src/collection/agent-collection/AgentCollection';
3
+ import { DEFAULT_MAX_RECURSION } from '../../../../src/config';
4
+ import { NotFoundError } from '../../../../src/errors/NotFoundError';
5
+ import { ParseError } from '../../../../src/errors/ParseError';
6
+ import type { string_agent_url } from '../../../../src/types/typeAliases';
7
+ import { spaceTrim } from '../../../../src/utils/organization/spaceTrim';
8
+ import type { FederatedAgentImportConfiguration } from '../constants/federatedAgentImport';
9
+ import {
10
+ resolveBookScopedAgentContext,
11
+ type ResolvedBookScopedAgentContext,
12
+ } from './agentReferenceResolver/bookScopedAgentReferences';
13
+ import {
14
+ createLocalAgentUrl,
15
+ normalizeLocalAgentUrlReferences,
16
+ normalizeLocalServerUrls,
17
+ resolveLocalAgentRouteReference,
18
+ } from './localAgentRouteReferences';
19
+ import { createMissingImportedAgentFallback } from './createMissingImportedAgentFallback';
20
+ import {
21
+ resolveInheritedAgentSource,
22
+ type AgentSourceImporter,
23
+ } from './resolveInheritedAgentSource';
24
+
25
+ /**
26
+ * Dependencies needed for importing same-instance agent sources without HTTP.
27
+ */
28
+ export type CreateLocalAgentSourceImporterOptions = {
29
+ /**
30
+ * Collection used to load local regular and book-scoped agent sources.
31
+ */
32
+ readonly collection: Pick<AgentCollection, 'getAgentPermanentId' | 'getAgentSource'>;
33
+
34
+ /**
35
+ * Server origins that should be treated as this Agents Server instance.
36
+ */
37
+ readonly localServerUrls: ReadonlyArray<string>;
38
+
39
+ /**
40
+ * Exact agent URLs that should be treated as local even when their origin is not in `localServerUrls`.
41
+ */
42
+ readonly localAgentUrls?: ReadonlyArray<string_agent_url>;
43
+
44
+ /**
45
+ * Adam agent URL used for implicit default inheritance.
46
+ */
47
+ readonly adamAgentUrl: string_agent_url;
48
+
49
+ /**
50
+ * Resolver used for compact references that are not book-scoped.
51
+ */
52
+ readonly fallbackResolver?: AgentReferenceResolver;
53
+
54
+ /**
55
+ * Retry configuration used when nested imports leave the local server.
56
+ */
57
+ readonly federatedAgentImportConfiguration?: FederatedAgentImportConfiguration;
58
+ };
59
+
60
+ /**
61
+ * Creates an importer that loads same-instance agent URLs directly from the collection.
62
+ *
63
+ * @param options - Same-instance import dependencies.
64
+ * @returns Importer suitable for `resolveInheritedAgentSource`.
65
+ */
66
+ export function createLocalAgentSourceImporter(options: CreateLocalAgentSourceImporterOptions): AgentSourceImporter {
67
+ const localServerUrls = normalizeLocalServerUrls(options.localServerUrls);
68
+ const localAgentUrlReferences = normalizeLocalAgentUrlReferences(options.localAgentUrls || []);
69
+ const localAgentSourceImporter: AgentSourceImporter = async (agentUrl, context) => {
70
+ const localRouteReference = resolveLocalAgentRouteReference(agentUrl, localServerUrls, localAgentUrlReferences);
71
+
72
+ if (!localRouteReference) {
73
+ return null;
74
+ }
75
+
76
+ const nextRecursionLevel = (context.importAgentOptions.recursionLevel || 0) + 1;
77
+ assertRecursionLevel(nextRecursionLevel, agentUrl);
78
+
79
+ let resolvedAgentContext: ResolvedBookScopedAgentContext;
80
+
81
+ try {
82
+ resolvedAgentContext = await resolveBookScopedAgentContext({
83
+ collection: options.collection,
84
+ agentIdentifier: localRouteReference.agentIdentifier,
85
+ localServerUrl: localRouteReference.localServerUrl,
86
+ fallbackResolver: options.fallbackResolver,
87
+ });
88
+ } catch (error) {
89
+ if (error instanceof NotFoundError) {
90
+ return createMissingImportedAgentFallback(agentUrl, 1, error);
91
+ }
92
+
93
+ throw error;
94
+ }
95
+
96
+ return resolveInheritedAgentSource(resolvedAgentContext.unresolvedAgentSource, {
97
+ adamAgentUrl: options.adamAgentUrl,
98
+ recursionLevel: nextRecursionLevel,
99
+ inheritancePath: context.importAgentOptions.inheritancePath,
100
+ currentAgentUrl: resolvedAgentContext.canonicalAgentUrl,
101
+ currentAgentAliases: createCurrentAgentAliases(resolvedAgentContext, localServerUrls),
102
+ agentReferenceResolver: resolvedAgentContext.scopedAgentReferenceResolver,
103
+ federatedAgentImportConfiguration:
104
+ options.federatedAgentImportConfiguration || context.federatedAgentImportConfiguration,
105
+ agentSourceImporter: localAgentSourceImporter,
106
+ });
107
+ };
108
+
109
+ return localAgentSourceImporter;
110
+ }
111
+
112
+ /**
113
+ * Throws when direct local importing would exceed the configured recursion limit.
114
+ *
115
+ * @param recursionLevel - Next recursion level.
116
+ * @param agentUrl - Local agent URL being imported.
117
+ */
118
+ function assertRecursionLevel(recursionLevel: number, agentUrl: string_agent_url): void {
119
+ if (recursionLevel <= DEFAULT_MAX_RECURSION) {
120
+ return;
121
+ }
122
+
123
+ throw new ParseError(
124
+ spaceTrim(
125
+ (block) => `
126
+ Recursion depth ${recursionLevel} exceeds maximum allowed ${DEFAULT_MAX_RECURSION} while importing local agent:
127
+
128
+ ${block(agentUrl)}
129
+ `,
130
+ ),
131
+ );
132
+ }
133
+
134
+ /**
135
+ * Builds cycle-detection aliases for every same-instance origin.
136
+ *
137
+ * @param resolvedAgentContext - Resolved local/book-scoped agent context.
138
+ * @param localServerUrls - Same-instance server origins.
139
+ * @returns Unique local URL aliases for the imported agent.
140
+ */
141
+ function createCurrentAgentAliases(
142
+ resolvedAgentContext: ResolvedBookScopedAgentContext,
143
+ localServerUrls: ReadonlyArray<string>,
144
+ ): Array<string_agent_url> {
145
+ const agentIdentifiers = [
146
+ resolvedAgentContext.canonicalAgentIdentifier,
147
+ resolvedAgentContext.requestedAgentIdentifier,
148
+ resolvedAgentContext.resolvedAgentName,
149
+ ].filter((agentIdentifier, index, agentIdentifierList) => agentIdentifierList.indexOf(agentIdentifier) === index);
150
+ const agentAliases = new Set<string>(resolvedAgentContext.currentAgentAliases);
151
+
152
+ for (const localServerUrl of localServerUrls) {
153
+ for (const agentIdentifier of agentIdentifiers) {
154
+ agentAliases.add(createLocalAgentUrl(localServerUrl, agentIdentifier));
155
+ }
156
+ }
157
+
158
+ return [...agentAliases] as Array<string_agent_url>;
159
+ }
@@ -0,0 +1,60 @@
1
+ import { padBook } from '../../../../src/book-2.0/agent-source/padBook';
2
+ import { validateBook } from '../../../../src/book-2.0/agent-source/string_book';
3
+ import type { string_agent_url } from '../../../../src/types/typeAliases';
4
+ import type { string_book } from '../../../../src/book-2.0/agent-source/string_book';
5
+ import { spaceTrim } from '../../../../src/utils/organization/spaceTrim';
6
+
7
+ /**
8
+ * Collapses one thrown error into a single-line message suitable for `NOTE` commitments.
9
+ *
10
+ * @param agentUrl - Canonical imported agent URL.
11
+ * @param error - Unknown error raised while loading the imported agent.
12
+ * @returns Human-readable single-line failure reason.
13
+ *
14
+ * @private internal helper for imported-agent fallback books
15
+ */
16
+ function normalizeImportFailureMessage(agentUrl: string_agent_url, error: unknown): string {
17
+ const rawMessage = error instanceof Error ? error.message : String(error);
18
+ const normalizedMessage = rawMessage.replace(/\s+/g, ' ').trim();
19
+ const prefix = `Failed to import agent from "${agentUrl}"`;
20
+
21
+ if (normalizedMessage.startsWith(prefix)) {
22
+ const suffix = normalizedMessage.slice(prefix.length).trim();
23
+ if (suffix.length > 0) {
24
+ return suffix.replace(/^[-:,\s]+/, '').trim() || 'unknown error';
25
+ }
26
+ }
27
+
28
+ return normalizedMessage || 'unknown error';
29
+ }
30
+
31
+ /**
32
+ * Creates the ad-hoc fallback source returned when a referenced agent stays unavailable.
33
+ *
34
+ * @param agentUrl - Canonical imported agent URL.
35
+ * @param attempts - Total number of attempts that were used.
36
+ * @param error - Final error raised by the failed import attempts.
37
+ * @returns Valid fallback book source.
38
+ *
39
+ * @private internal helper for imported-agent fallback books
40
+ */
41
+ export function createMissingImportedAgentFallback(
42
+ agentUrl: string_agent_url,
43
+ attempts: number,
44
+ error: unknown,
45
+ ): string_book {
46
+ const failureMessage = normalizeImportFailureMessage(agentUrl, error);
47
+
48
+ return padBook(
49
+ validateBook(
50
+ spaceTrim(
51
+ `
52
+ Not found agent
53
+
54
+ NOTE This agent was supposed to be imported from ${agentUrl}, but it can not be loaded after ${attempts} attempts because of ${failureMessage}
55
+ CLOSED
56
+ `,
57
+ ),
58
+ ),
59
+ );
60
+ }
@@ -1,13 +1,30 @@
1
1
  import type { SupabaseClient } from '@supabase/supabase-js';
2
- import type { AgentBasicInformation, string_book } from '../../../../src/_packages/types.index';
2
+ import type {
3
+ AgentBasicInformation,
4
+ string_agent_permanent_id,
5
+ string_agent_url,
6
+ string_book,
7
+ } from '../../../../src/_packages/types.index';
8
+ import type { AgentReferenceResolver } from '../../../../src/book-2.0/agent-source/AgentReferenceResolver';
3
9
  import type { AgentCollection } from '../../../../src/collection/agent-collection/AgentCollection';
10
+ import { DEFAULT_MAX_RECURSION } from '../../../../src/config';
11
+ import { NotFoundError } from '../../../../src/errors/NotFoundError';
12
+ import { ParseError } from '../../../../src/errors/ParseError';
13
+ import { spaceTrim } from '../../../../src/utils/organization/spaceTrim';
4
14
  import { normalizeDomainForMatching } from '../../../../src/utils/validators/url/normalizeDomainForMatching';
5
15
  import type { FederatedAgentImportConfiguration } from '../constants/federatedAgentImport';
6
16
  import { createServerAgentReferenceResolver } from './agentReferenceResolver/createServerAgentReferenceResolver';
7
17
  import { loadFederatedAgentImportConfiguration } from './federatedAgentImportConfiguration';
8
18
  import { getFederatedServers } from './getFederatedServers';
9
19
  import { getWellKnownAgentUrl } from './getWellKnownAgentUrl';
10
- import { resolveInheritedAgentSource } from './resolveInheritedAgentSource';
20
+ import {
21
+ createLocalAgentUrl,
22
+ normalizeLocalAgentUrlReferences,
23
+ normalizeLocalServerUrls,
24
+ resolveLocalAgentRouteReference,
25
+ } from './localAgentRouteReferences';
26
+ import { createMissingImportedAgentFallback } from './createMissingImportedAgentFallback';
27
+ import { resolveInheritedAgentSource, type AgentSourceImporter } from './resolveInheritedAgentSource';
11
28
  import { createServerPublicUrl, type ServerRecord } from './serverRegistry';
12
29
 
13
30
  /**
@@ -111,11 +128,6 @@ type CustomDomainAgentRow = {
111
128
  readonly agentSource: string;
112
129
  };
113
130
 
114
- /**
115
- * Minimal agent identity row used to initialize compact-reference resolution.
116
- */
117
- type CustomDomainAgentReferenceRow = Pick<CustomDomainAgentRow, 'agentName' | 'permanentId'>;
118
-
119
131
  /**
120
132
  * Minimal resolved metadata needed for custom-domain matching.
121
133
  */
@@ -127,7 +139,20 @@ type ResolvedCustomDomainMetadata = Pick<AgentBasicInformation, 'links' | 'meta'
127
139
  * @param agents - Stored server-owned agents.
128
140
  * @returns Lightweight collection compatible with the resolver initialization step.
129
141
  */
130
- function createResolverAgentCollection(agents: ReadonlyArray<CustomDomainAgentReferenceRow>): AgentCollection {
142
+ function createResolverAgentCollection(agents: ReadonlyArray<CustomDomainAgentRow>): AgentCollection {
143
+ const resolveAgent = (agentNameOrPermanentId: string): CustomDomainAgentRow => {
144
+ const agent = agents.find(
145
+ (candidate) =>
146
+ candidate.agentName === agentNameOrPermanentId || candidate.permanentId === agentNameOrPermanentId,
147
+ );
148
+
149
+ if (!agent) {
150
+ throw new NotFoundError(`Agent with name or id "${agentNameOrPermanentId}" not found`);
151
+ }
152
+
153
+ return agent;
154
+ };
155
+
131
156
  return {
132
157
  async listAgents() {
133
158
  return agents.map(
@@ -147,6 +172,13 @@ function createResolverAgentCollection(agents: ReadonlyArray<CustomDomainAgentRe
147
172
  } satisfies AgentBasicInformation),
148
173
  );
149
174
  },
175
+ async getAgentPermanentId(agentNameOrPermanentId: string) {
176
+ const agent = resolveAgent(agentNameOrPermanentId);
177
+ return (agent.permanentId || agent.agentName) as string_agent_permanent_id;
178
+ },
179
+ async getAgentSource(agentNameOrPermanentId: string) {
180
+ return resolveAgent(agentNameOrPermanentId).agentSource as string_book;
181
+ },
150
182
  } as unknown as AgentCollection;
151
183
  }
152
184
 
@@ -181,6 +213,108 @@ function createCanonicalLocalAgentUrl(agent: CustomDomainAgentRow, localServerUr
181
213
  return `${localServerUrl.replace(/\/+$/g, '')}/agents/${encodeURIComponent(canonicalAgentIdentifier)}`;
182
214
  }
183
215
 
216
+ /**
217
+ * Creates an Edge-safe local importer for custom-domain metadata resolution.
218
+ *
219
+ * @param options - Local collection and inheritance dependencies.
220
+ * @returns Local source importer for route-level agent URLs.
221
+ */
222
+ function createCustomDomainLocalAgentSourceImporter(options: {
223
+ readonly collection: Pick<AgentCollection, 'getAgentPermanentId' | 'getAgentSource'>;
224
+ readonly localServerUrl: string;
225
+ readonly adamAgentUrl: string_agent_url;
226
+ readonly agentReferenceResolver: AgentReferenceResolver;
227
+ readonly federatedAgentImportConfiguration: FederatedAgentImportConfiguration;
228
+ }): AgentSourceImporter {
229
+ const localServerUrls = normalizeLocalServerUrls([options.localServerUrl]);
230
+ const localAgentUrlReferences = normalizeLocalAgentUrlReferences([options.adamAgentUrl]);
231
+ const agentSourceImporter: AgentSourceImporter = async (agentUrl, context) => {
232
+ const localRouteReference = resolveLocalAgentRouteReference(agentUrl, localServerUrls, localAgentUrlReferences);
233
+
234
+ if (!localRouteReference) {
235
+ return null;
236
+ }
237
+
238
+ const nextRecursionLevel = (context.importAgentOptions.recursionLevel || 0) + 1;
239
+ assertCustomDomainLocalImportRecursionLevel(nextRecursionLevel, agentUrl);
240
+
241
+ let agentPermanentId: string_agent_permanent_id;
242
+ let unresolvedAgentSource: string_book;
243
+
244
+ try {
245
+ agentPermanentId = await options.collection.getAgentPermanentId(localRouteReference.agentIdentifier);
246
+ unresolvedAgentSource = await options.collection.getAgentSource(agentPermanentId);
247
+ } catch (error) {
248
+ if (error instanceof NotFoundError) {
249
+ return createMissingImportedAgentFallback(agentUrl, 1, error);
250
+ }
251
+
252
+ throw error;
253
+ }
254
+
255
+ const resolvedAgentName = readAgentSourceTitle(unresolvedAgentSource) || localRouteReference.agentIdentifier;
256
+ const canonicalAgentUrl = createLocalAgentUrl(localRouteReference.localServerUrl, agentPermanentId);
257
+
258
+ return resolveInheritedAgentSource(unresolvedAgentSource, {
259
+ adamAgentUrl: options.adamAgentUrl,
260
+ recursionLevel: nextRecursionLevel,
261
+ inheritancePath: context.importAgentOptions.inheritancePath,
262
+ currentAgentUrl: canonicalAgentUrl,
263
+ currentAgentAliases: [
264
+ canonicalAgentUrl,
265
+ createLocalAgentUrl(localRouteReference.localServerUrl, localRouteReference.agentIdentifier),
266
+ createLocalAgentUrl(localRouteReference.localServerUrl, resolvedAgentName),
267
+ ].filter((value, index, values): value is string_agent_url => values.indexOf(value) === index),
268
+ agentReferenceResolver: options.agentReferenceResolver,
269
+ federatedAgentImportConfiguration:
270
+ options.federatedAgentImportConfiguration || context.federatedAgentImportConfiguration,
271
+ agentSourceImporter,
272
+ });
273
+ };
274
+
275
+ return agentSourceImporter;
276
+ }
277
+
278
+ /**
279
+ * Reads the first non-empty title line from one book source.
280
+ *
281
+ * @param agentSource - Agent book source.
282
+ * @returns Title or `null` when the source has no title line.
283
+ */
284
+ function readAgentSourceTitle(agentSource: string_book): string | null {
285
+ for (const line of agentSource.split(/\r?\n/)) {
286
+ const title = line.trim();
287
+
288
+ if (title) {
289
+ return title;
290
+ }
291
+ }
292
+
293
+ return null;
294
+ }
295
+
296
+ /**
297
+ * Throws when direct local importing would exceed the configured recursion limit.
298
+ *
299
+ * @param recursionLevel - Next recursion level.
300
+ * @param agentUrl - Local agent URL being imported.
301
+ */
302
+ function assertCustomDomainLocalImportRecursionLevel(recursionLevel: number, agentUrl: string_agent_url): void {
303
+ if (recursionLevel <= DEFAULT_MAX_RECURSION) {
304
+ return;
305
+ }
306
+
307
+ throw new ParseError(
308
+ spaceTrim(
309
+ (block) => `
310
+ Recursion depth ${recursionLevel} exceeds maximum allowed ${DEFAULT_MAX_RECURSION} while importing local custom-domain agent:
311
+
312
+ ${block(agentUrl)}
313
+ `,
314
+ ),
315
+ );
316
+ }
317
+
184
318
  /**
185
319
  * Parses only the resolved custom-domain metadata used by proxy routing.
186
320
  *
@@ -254,6 +388,7 @@ async function resolveCustomDomainMetadataForAgent(
254
388
  readonly adamAgentUrl: string;
255
389
  readonly agentReferenceResolver: Awaited<ReturnType<typeof createServerAgentReferenceResolver>>;
256
390
  readonly federatedAgentImportConfiguration: FederatedAgentImportConfiguration;
391
+ readonly agentSourceImporter: AgentSourceImporter;
257
392
  },
258
393
  ): Promise<ResolvedCustomDomainMetadata> {
259
394
  const resolvedAgentSource = await resolveInheritedAgentSource(agent.agentSource as string_book, {
@@ -261,6 +396,7 @@ async function resolveCustomDomainMetadataForAgent(
261
396
  currentAgentUrl: createCanonicalLocalAgentUrl(agent, options.localServerUrl),
262
397
  agentReferenceResolver: options.agentReferenceResolver,
263
398
  federatedAgentImportConfiguration: options.federatedAgentImportConfiguration,
399
+ agentSourceImporter: options.agentSourceImporter,
264
400
  });
265
401
 
266
402
  return parseResolvedCustomDomainMetadata(resolvedAgentSource);
@@ -309,7 +445,7 @@ export async function resolveCustomDomainAgent(
309
445
 
310
446
  const { data: resolverReferenceAgents, error: resolverReferenceError } = await supabase
311
447
  .from(tableName)
312
- .select('agentName, permanentId')
448
+ .select('agentName, permanentId, agentSource')
313
449
  .is('deletedAt', null);
314
450
 
315
451
  if (resolverReferenceError || !Array.isArray(resolverReferenceAgents)) {
@@ -317,13 +453,21 @@ export async function resolveCustomDomainAgent(
317
453
  }
318
454
 
319
455
  const localServerUrl = createServerPublicUrl(server.domain).href;
456
+ const agentCollection = createResolverAgentCollection(
457
+ resolverReferenceAgents as Array<CustomDomainAgentRow>,
458
+ );
320
459
  const agentReferenceResolver = await createServerAgentReferenceResolver({
321
- agentCollection: createResolverAgentCollection(
322
- resolverReferenceAgents as Array<CustomDomainAgentReferenceRow>,
323
- ),
460
+ agentCollection,
324
461
  localServerUrl,
325
462
  federatedServers,
326
463
  });
464
+ const agentSourceImporter = createCustomDomainLocalAgentSourceImporter({
465
+ collection: agentCollection,
466
+ localServerUrl,
467
+ adamAgentUrl,
468
+ agentReferenceResolver,
469
+ federatedAgentImportConfiguration,
470
+ });
327
471
 
328
472
  let matchedAgent: CustomDomainAgentRow | undefined;
329
473
 
@@ -333,6 +477,7 @@ export async function resolveCustomDomainAgent(
333
477
  adamAgentUrl,
334
478
  agentReferenceResolver,
335
479
  federatedAgentImportConfiguration,
480
+ agentSourceImporter,
336
481
  });
337
482
 
338
483
  if (matchesResolvedCustomDomain(resolvedMetadata, candidates)) {
@@ -153,7 +153,7 @@ async function enqueueExternalUserChatJob(job: UserChatJobRecord): Promise<Proce
153
153
  return { didMutate: false, outcome: 'waiting' };
154
154
  }
155
155
 
156
- const threadMessages = createUserChatRunnerThreadMessages({
156
+ const threadMessages = await createUserChatRunnerThreadMessages({
157
157
  messages: chat.messages,
158
158
  userMessageId: job.userMessageId,
159
159
  resolveInitialAgentMessage: () => parseAgentSource(agentSourceSnapshot.agentSource).initialMessage,
@@ -1,12 +1,10 @@
1
- import { padBook } from '../../../../src/book-2.0/agent-source/padBook';
2
- import { validateBook } from '../../../../src/book-2.0/agent-source/string_book';
3
1
  import type { string_agent_url } from '../../../../src/types/typeAliases';
4
2
  import type { string_book } from '../../../../src/book-2.0/agent-source/string_book';
5
- import { spaceTrim } from '../../../../src/utils/organization/spaceTrim';
6
3
  import {
7
4
  DEFAULT_FEDERATED_AGENT_IMPORT_CONFIGURATION,
8
5
  type FederatedAgentImportConfiguration,
9
6
  } from '../constants/federatedAgentImport';
7
+ import { createMissingImportedAgentFallback } from './createMissingImportedAgentFallback';
10
8
  import { retryWithBackoff } from './retryWithBackoff';
11
9
  import { importAgent, type ImportAgentOptions } from './importAgent';
12
10
 
@@ -40,61 +38,6 @@ const cachedFailedImportedAgentFallbackByKey = new Map<string, FailedImportedAge
40
38
  */
41
39
  const pendingImportedAgentFallbackByKey = new Map<string, Promise<string_book>>();
42
40
 
43
- /**
44
- * Collapses one thrown error into a single-line message suitable for `NOTE` commitments.
45
- *
46
- * @param agentUrl - Canonical imported agent URL.
47
- * @param error - Unknown error raised while loading the imported agent.
48
- * @returns Human-readable single-line failure reason.
49
- *
50
- * @private internal helper for imported-agent fallback books
51
- */
52
- function normalizeImportFailureMessage(agentUrl: string_agent_url, error: unknown): string {
53
- const rawMessage = error instanceof Error ? error.message : String(error);
54
- const normalizedMessage = rawMessage.replace(/\s+/g, ' ').trim();
55
- const prefix = `Failed to import agent from "${agentUrl}"`;
56
-
57
- if (normalizedMessage.startsWith(prefix)) {
58
- const suffix = normalizedMessage.slice(prefix.length).trim();
59
- if (suffix.length > 0) {
60
- return suffix.replace(/^[-:,\s]+/, '').trim() || 'unknown error';
61
- }
62
- }
63
-
64
- return normalizedMessage || 'unknown error';
65
- }
66
-
67
- /**
68
- * Creates the ad-hoc fallback source returned when a federated imported agent stays unavailable.
69
- *
70
- * @param agentUrl - Canonical imported agent URL.
71
- * @param attempts - Total number of attempts that were used.
72
- * @param error - Final error raised by the failed import attempts.
73
- * @returns Valid fallback book source.
74
- *
75
- * @private internal helper for imported-agent fallback books
76
- */
77
- function createMissingImportedAgentFallback(
78
- agentUrl: string_agent_url,
79
- attempts: number,
80
- error: unknown,
81
- ): string_book {
82
- const failureMessage = normalizeImportFailureMessage(agentUrl, error);
83
-
84
- return padBook(
85
- validateBook(
86
- spaceTrim(
87
- `
88
- Not found agent
89
-
90
- NOTE This agent was supposed to be imported from ${agentUrl}, but it can not be loaded after ${attempts} attempts because of ${failureMessage}
91
- CLOSED
92
- `,
93
- ),
94
- ),
95
- );
96
- }
97
-
98
41
  /**
99
42
  * Builds the stable cache key used for import-with-fallback memoization.
100
43
  *