@illuma-ai/agents 1.4.0-alpha.2 → 1.4.0-alpha.4

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 (51) hide show
  1. package/dist/cjs/main.cjs +37 -27
  2. package/dist/cjs/main.cjs.map +1 -1
  3. package/dist/cjs/providers/tools-server/ToolsServerCapabilityProvider.cjs +4 -0
  4. package/dist/cjs/providers/tools-server/ToolsServerCapabilityProvider.cjs.map +1 -1
  5. package/dist/cjs/providers/types.cjs.map +1 -1
  6. package/dist/cjs/tools/artifacts/schema.cjs +86 -0
  7. package/dist/cjs/tools/artifacts/schema.cjs.map +1 -0
  8. package/dist/cjs/tools/artifacts/tool.cjs +219 -0
  9. package/dist/cjs/tools/artifacts/tool.cjs.map +1 -0
  10. package/dist/cjs/tools/fileSearch/formatter.cjs +2 -4
  11. package/dist/cjs/tools/fileSearch/formatter.cjs.map +1 -1
  12. package/dist/cjs/tools/fileSearch/ragClient.cjs +4 -6
  13. package/dist/cjs/tools/fileSearch/ragClient.cjs.map +1 -1
  14. package/dist/cjs/tools/fileSearch/schema.cjs.map +1 -1
  15. package/dist/cjs/tools/fileSearch/tool.cjs.map +1 -1
  16. package/dist/esm/main.mjs +2 -0
  17. package/dist/esm/main.mjs.map +1 -1
  18. package/dist/esm/providers/tools-server/ToolsServerCapabilityProvider.mjs +4 -0
  19. package/dist/esm/providers/tools-server/ToolsServerCapabilityProvider.mjs.map +1 -1
  20. package/dist/esm/providers/types.mjs.map +1 -1
  21. package/dist/esm/tools/artifacts/schema.mjs +79 -0
  22. package/dist/esm/tools/artifacts/schema.mjs.map +1 -0
  23. package/dist/esm/tools/artifacts/tool.mjs +213 -0
  24. package/dist/esm/tools/artifacts/tool.mjs.map +1 -0
  25. package/dist/esm/tools/fileSearch/formatter.mjs +2 -4
  26. package/dist/esm/tools/fileSearch/formatter.mjs.map +1 -1
  27. package/dist/esm/tools/fileSearch/ragClient.mjs +4 -6
  28. package/dist/esm/tools/fileSearch/ragClient.mjs.map +1 -1
  29. package/dist/esm/tools/fileSearch/schema.mjs.map +1 -1
  30. package/dist/esm/tools/fileSearch/tool.mjs.map +1 -1
  31. package/dist/types/index.d.ts +1 -0
  32. package/dist/types/providers/types.d.ts +14 -0
  33. package/dist/types/tools/artifacts/index.d.ts +3 -0
  34. package/dist/types/tools/artifacts/schema.d.ts +63 -0
  35. package/dist/types/tools/artifacts/tool.d.ts +16 -0
  36. package/dist/types/tools/artifacts/types.d.ts +127 -0
  37. package/package.json +1 -1
  38. package/src/index.ts +1 -0
  39. package/src/providers/tools-server/ToolsServerCapabilityProvider.ts +8 -0
  40. package/src/providers/types.ts +17 -0
  41. package/src/tools/artifacts/__tests__/tool.test.ts +259 -0
  42. package/src/tools/artifacts/index.ts +33 -0
  43. package/src/tools/artifacts/schema.ts +99 -0
  44. package/src/tools/artifacts/tool.ts +289 -0
  45. package/src/tools/artifacts/types.ts +162 -0
  46. package/src/tools/fileSearch/__tests__/tool.test.ts +20 -10
  47. package/src/tools/fileSearch/formatter.ts +5 -7
  48. package/src/tools/fileSearch/ragClient.ts +6 -10
  49. package/src/tools/fileSearch/schema.ts +2 -2
  50. package/src/tools/fileSearch/tool.ts +6 -6
  51. package/src/tools/fileSearch/types.ts +4 -2
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Artifact-tool types. The library owns the LangChain wiring, schema, and
3
+ * action dispatch. Runtime supplies an `ArtifactHandlers` bundle — one
4
+ * function per action — so ranger reuses its existing handlers, CLI
5
+ * supplies disk-backed ones, and A2A server supplies buffer-backed ones.
6
+ *
7
+ * Each handler receives:
8
+ * - `args`: the parsed input (typed per action)
9
+ * - `scope`: runtime-resolved scope identity (conversationId + userId
10
+ * for ranger, agent-run-id for CLI, a2a-task-id for A2A)
11
+ *
12
+ * Each handler returns a `[llmText, toolArtifact]` tuple matching
13
+ * LangChain's `content_and_artifact` response format.
14
+ */
15
+
16
+ export interface ArtifactToolScope {
17
+ /**
18
+ * Primary scope — whatever the runtime uses to silo artifacts per
19
+ * session/run/conversation. Ranger uses `conversationId`; CLI uses
20
+ * `agent-run-id` or `agent-id`; A2A uses `a2a-task-id`.
21
+ */
22
+ conversationId: string;
23
+ /** Optional — present when the runtime has an authenticated user. */
24
+ userId?: string;
25
+ /** Free-form extension — runtimes can add their own fields. */
26
+ [key: string]: unknown;
27
+ }
28
+
29
+ export type ArtifactToolResult = [llmText: string, artifact?: unknown];
30
+
31
+ // ─── Action arg shapes ────────────────────────────────────────────────────
32
+
33
+ export interface WriteArgs {
34
+ action: 'write';
35
+ content_id?: string;
36
+ content: string;
37
+ name?: string;
38
+ }
39
+
40
+ export interface EditArgs {
41
+ action: 'edit';
42
+ content_id: string;
43
+ old_str: string;
44
+ new_str: string;
45
+ replace_all?: boolean;
46
+ }
47
+
48
+ export interface VerifyArgs {
49
+ action: 'verify';
50
+ content_id: string;
51
+ }
52
+
53
+ export interface DeleteArgs {
54
+ action: 'delete';
55
+ content_id: string;
56
+ }
57
+
58
+ export interface ReadArgs {
59
+ action: 'read';
60
+ content_id: string;
61
+ start_line?: number;
62
+ end_line?: number;
63
+ offset?: number;
64
+ limit?: number;
65
+ }
66
+
67
+ export interface SearchArgs {
68
+ action: 'search';
69
+ content_id: string;
70
+ pattern: string;
71
+ flags?: string;
72
+ context?: number;
73
+ offset?: number;
74
+ limit?: number;
75
+ }
76
+
77
+ export interface ListArgs {
78
+ action: 'list';
79
+ }
80
+
81
+ export interface InfoArgs {
82
+ action: 'info';
83
+ content_id: string;
84
+ }
85
+
86
+ export type ArtifactWriteAction =
87
+ | WriteArgs
88
+ | EditArgs
89
+ | VerifyArgs
90
+ | DeleteArgs;
91
+ export type ArtifactReadAction = ReadArgs | SearchArgs | ListArgs | InfoArgs;
92
+
93
+ // ─── Handler bundles ──────────────────────────────────────────────────────
94
+
95
+ /** Writer-surface handlers — invoked by `artifact_tool`. */
96
+ export interface ArtifactWriteHandlers {
97
+ write(args: WriteArgs, scope: ArtifactToolScope): Promise<ArtifactToolResult>;
98
+ edit(args: EditArgs, scope: ArtifactToolScope): Promise<ArtifactToolResult>;
99
+ verify(
100
+ args: VerifyArgs,
101
+ scope: ArtifactToolScope
102
+ ): Promise<ArtifactToolResult>;
103
+ delete(
104
+ args: DeleteArgs,
105
+ scope: ArtifactToolScope
106
+ ): Promise<ArtifactToolResult>;
107
+ }
108
+
109
+ /** Reader-surface handlers — invoked by `content_reader`. */
110
+ export interface ContentReadHandlers {
111
+ read(args: ReadArgs, scope: ArtifactToolScope): Promise<ArtifactToolResult>;
112
+ search(
113
+ args: SearchArgs,
114
+ scope: ArtifactToolScope
115
+ ): Promise<ArtifactToolResult>;
116
+ list(args: ListArgs, scope: ArtifactToolScope): Promise<ArtifactToolResult>;
117
+ info(args: InfoArgs, scope: ArtifactToolScope): Promise<ArtifactToolResult>;
118
+ }
119
+
120
+ /**
121
+ * Optional content_id self-healing: runtimes that want to let the LLM
122
+ * pass nicknames (e.g., "Dashboard") instead of canonical IDs implement
123
+ * this. Library falls through when unset.
124
+ */
125
+ export interface ContentIdResolver {
126
+ resolve(
127
+ id: string,
128
+ scope: ArtifactToolScope
129
+ ): Promise<{ resolvedId: string; resolvedName?: string } | null>;
130
+ }
131
+
132
+ export interface ArtifactToolLogger {
133
+ debug: (msg: string, ...args: unknown[]) => void;
134
+ info: (msg: string, ...args: unknown[]) => void;
135
+ warn: (msg: string, ...args: unknown[]) => void;
136
+ error: (msg: string, ...args: unknown[]) => void;
137
+ }
138
+
139
+ export interface ArtifactToolBaseConfig {
140
+ /**
141
+ * Resolves the runtime scope from the LangChain `config` object on each
142
+ * invocation. Ranger pulls `conversationId` + `userId` from request
143
+ * metadata; CLI pulls `agent-run-id` from its runner context.
144
+ */
145
+ getScope: (config: unknown) => ArtifactToolScope | null;
146
+ resolver?: ContentIdResolver;
147
+ logger?: ArtifactToolLogger;
148
+ /**
149
+ * Description override. Host can inject brand-specific guidance
150
+ * (e.g., ranger's HTML branding mandate, TSX style rules). Defaults
151
+ * to a generic description appropriate for any runtime.
152
+ */
153
+ descriptionOverride?: string;
154
+ }
155
+
156
+ export interface ArtifactToolConfig extends ArtifactToolBaseConfig {
157
+ handlers: ArtifactWriteHandlers;
158
+ }
159
+
160
+ export interface ContentReaderToolConfig extends ArtifactToolBaseConfig {
161
+ handlers: ContentReadHandlers;
162
+ }
@@ -8,16 +8,26 @@
8
8
  */
9
9
 
10
10
  import { createFileSearchTool } from '../tool';
11
- import { plainTextFormatter, createCitationAnchorFormatter } from '../formatter';
12
- import type { RagClient, RagQueryParams, RagChunk, FileSearchFile } from '../types';
11
+ import {
12
+ plainTextFormatter,
13
+ createCitationAnchorFormatter,
14
+ } from '../formatter';
15
+ import type {
16
+ RagClient,
17
+ RagQueryParams,
18
+ RagChunk,
19
+ FileSearchFile,
20
+ } from '../types';
13
21
 
14
22
  // Build a mock RagClient that records every query it receives and returns
15
23
  // a deterministic chunk set per file.
16
- function makeRagClient(opts: {
17
- chunksByFile?: Record<string, RagChunk[]>;
18
- failFileIds?: Set<string>;
19
- hang?: boolean;
20
- } = {}) {
24
+ function makeRagClient(
25
+ opts: {
26
+ chunksByFile?: Record<string, RagChunk[]>;
27
+ failFileIds?: Set<string>;
28
+ hang?: boolean;
29
+ } = {}
30
+ ) {
21
31
  const calls: RagQueryParams[] = [];
22
32
  const client: RagClient = {
23
33
  async query(params) {
@@ -119,7 +129,7 @@ describe('createFileSearchTool', () => {
119
129
  expect(calls.length).toBe(3);
120
130
  expect(onFileError).toHaveBeenCalledWith(
121
131
  expect.objectContaining({ file_id: 'f-beta' }),
122
- expect.any(Error),
132
+ expect.any(Error)
123
133
  );
124
134
  });
125
135
 
@@ -142,7 +152,7 @@ describe('createFileSearchTool', () => {
142
152
  entity_id: 'tenant-42',
143
153
  scope: 'user:alice',
144
154
  authHeaders: { Authorization: 'Bearer TOKEN' },
145
- }),
155
+ })
146
156
  );
147
157
  });
148
158
 
@@ -236,7 +246,7 @@ function chunk(
236
246
  file_id: string,
237
247
  text: string,
238
248
  distance: number,
239
- metadata?: Record<string, unknown>,
249
+ metadata?: Record<string, unknown>
240
250
  ): RagChunk {
241
251
  return { file_id, page_content: text, distance, metadata };
242
252
  }
@@ -63,14 +63,14 @@ export interface CitationAnchorFormatterOptions {
63
63
  }
64
64
 
65
65
  export function createCitationAnchorFormatter(
66
- opts: CitationAnchorFormatterOptions = {},
66
+ opts: CitationAnchorFormatterOptions = {}
67
67
  ): FileSearchResultFormatter {
68
68
  const toolName = opts.toolName ?? 'file_search';
69
- const getOffset = opts.getSourceOffset ?? (() => 0);
70
- const advance = opts.advanceSourceOffset ?? (() => {});
69
+ const getOffset = opts.getSourceOffset ?? ((): number => 0);
70
+ const advance = opts.advanceSourceOffset ?? ((_by: number): void => {});
71
71
 
72
72
  return {
73
- format(chunks) {
73
+ format(chunks): { message: string; artifact?: unknown } {
74
74
  if (chunks.length === 0) {
75
75
  return {
76
76
  message:
@@ -100,9 +100,7 @@ export function createCitationAnchorFormatter(
100
100
  relevance: 1 - c.distance,
101
101
  pages: getPage(c) != null ? [getPage(c) as number] : [],
102
102
  pageRelevance:
103
- getPage(c) != null
104
- ? { [getPage(c) as number]: 1 - c.distance }
105
- : {},
103
+ getPage(c) != null ? { [getPage(c) as number]: 1 - c.distance } : {},
106
104
  }));
107
105
 
108
106
  advance(chunks.length);
@@ -21,14 +21,11 @@ export const RAG_API_URL_ENV = 'RAG_API_URL';
21
21
 
22
22
  /** Resolve base URL at call time so env-var changes propagate. */
23
23
  export function getRagBaseUrl(override?: string): string {
24
- const url =
25
- override ??
26
- getEnvironmentVariable(RAG_API_URL_ENV) ??
27
- '';
24
+ const url = override ?? getEnvironmentVariable(RAG_API_URL_ENV) ?? '';
28
25
  if (!url) {
29
26
  throw new Error(
30
27
  `file_search: ${RAG_API_URL_ENV} is not configured. ` +
31
- `Set the env var or pass baseUrl to HttpRagClient.`,
28
+ `Set the env var or pass baseUrl to HttpRagClient.`
32
29
  );
33
30
  }
34
31
  return url.replace(/\/$/, '');
@@ -112,7 +109,7 @@ export class HttpRagClient implements RagClient {
112
109
  if (!res.ok) {
113
110
  const text = await res.text().catch(() => '');
114
111
  throw new Error(
115
- `RAG query failed: ${res.status} ${res.statusText} — ${text.slice(0, 200)}`,
112
+ `RAG query failed: ${res.status} ${res.statusText} — ${text.slice(0, 200)}`
116
113
  );
117
114
  }
118
115
  const json = (await res.json()) as RagApiResponse;
@@ -131,11 +128,10 @@ export class HttpRagClient implements RagClient {
131
128
  return resp
132
129
  .filter((row) => Array.isArray(row) && row.length === 2)
133
130
  .map(([doc, distance]) => ({
134
- file_id:
135
- (doc?.metadata?.file_id as string | undefined) ?? file_id,
136
- page_content: doc?.page_content ?? '',
131
+ file_id: (doc.metadata?.file_id as string | undefined) ?? file_id,
132
+ page_content: doc.page_content ?? '',
137
133
  distance: typeof distance === 'number' ? distance : 1,
138
- metadata: doc?.metadata,
134
+ metadata: doc.metadata,
139
135
  }));
140
136
  }
141
137
  }
@@ -4,13 +4,13 @@ export const fileSearchInputSchema = z.object({
4
4
  query: z
5
5
  .string()
6
6
  .describe(
7
- "A natural language query to search for relevant information in the files. Be SPECIFIC and TARGETED — use keywords for the specific section or topic you need. For comprehensive tasks (summaries, overviews), call this tool multiple times with different targeted queries (e.g., 'introduction', 'methodology', 'results', 'conclusions') rather than one broad query.",
7
+ "A natural language query to search for relevant information in the files. Be SPECIFIC and TARGETED — use keywords for the specific section or topic you need. For comprehensive tasks (summaries, overviews), call this tool multiple times with different targeted queries (e.g., 'introduction', 'methodology', 'results', 'conclusions') rather than one broad query."
8
8
  ),
9
9
  target_files: z
10
10
  .array(z.string())
11
11
  .optional()
12
12
  .describe(
13
- 'Optional list of filenames (or partial names) to limit the search to. When provided, only files whose name contains one of these strings will be searched. Use this to avoid searching irrelevant files. Omit to search all available files.',
13
+ 'Optional list of filenames (or partial names) to limit the search to. When provided, only files whose name contains one of these strings will be searched. Use this to avoid searching irrelevant files. Omit to search all available files.'
14
14
  ),
15
15
  });
16
16
 
@@ -54,7 +54,7 @@ Cite EVERY statement derived from file content. Place the citation anchor IMMEDI
54
54
  }
55
55
 
56
56
  export function createFileSearchTool(
57
- config: FileSearchToolConfig,
57
+ config: FileSearchToolConfig
58
58
  ): DynamicStructuredTool {
59
59
  const {
60
60
  ragClient,
@@ -97,16 +97,16 @@ export function createFileSearchTool(
97
97
  if (target_files && target_files.length > 0) {
98
98
  const lowerTargets = target_files.map((t) => t.toLowerCase());
99
99
  const matched = files.filter((f) =>
100
- lowerTargets.some((t) => f.filename.toLowerCase().includes(t)),
100
+ lowerTargets.some((t) => f.filename.toLowerCase().includes(t))
101
101
  );
102
102
  if (matched.length === 0) {
103
103
  logger?.warn(
104
- `[file_search] No files matched target_files ${target_files.join(', ')}; falling back to all files`,
104
+ `[file_search] No files matched target_files ${target_files.join(', ')}; falling back to all files`
105
105
  );
106
106
  filesToQuery = files;
107
107
  } else {
108
108
  logger?.info(
109
- `[file_search] Filtered to ${matched.length}/${files.length} via target_files`,
109
+ `[file_search] Filtered to ${matched.length}/${files.length} via target_files`
110
110
  );
111
111
  filesToQuery = matched;
112
112
  }
@@ -131,7 +131,7 @@ export function createFileSearchTool(
131
131
  } catch (err) {
132
132
  const e = err instanceof Error ? err : new Error(String(err));
133
133
  logger?.error(
134
- `[file_search] Query failed for ${file.filename}: ${e.message}`,
134
+ `[file_search] Query failed for ${file.filename}: ${e.message}`
135
135
  );
136
136
  callbacks?.onFileError?.(file, e);
137
137
  return [];
@@ -200,7 +200,7 @@ export function createFileSearchTool(
200
200
  responseFormat: 'content_and_artifact',
201
201
  description: buildDescription({ fileCitations }),
202
202
  schema: fileSearchInputSchema,
203
- },
203
+ }
204
204
  );
205
205
  }
206
206
 
@@ -79,7 +79,7 @@ export interface FileSearchResultFormatter {
79
79
  callIndex: number;
80
80
  /** Same files list the factory was seeded with (for lookups). */
81
81
  files: FileSearchFile[];
82
- },
82
+ }
83
83
  ): {
84
84
  /** Message returned to the LLM (content). */
85
85
  message: string;
@@ -122,7 +122,9 @@ export interface FileSearchToolConfig {
122
122
  * the host can mint fresh short-lived tokens (ranger's JWT pattern).
123
123
  * When omitted, no auth headers are sent.
124
124
  */
125
- getAuthHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
125
+ getAuthHeaders?: () =>
126
+ | Record<string, string>
127
+ | Promise<Record<string, string>>;
126
128
  /**
127
129
  * Result formatter. When omitted, the default plain-text formatter is
128
130
  * used (suitable for CLI/A2A runtimes that don't need citation anchors).