@open-mercato/ai-assistant 0.7.1-develop.7193.1.910a5b0a1e → 0.7.1-develop.7194.1.ab4fc81f82
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.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +10 -42
- package/README.md +12 -3
- package/dist/modules/ai_assistant/di.js +6 -1
- package/dist/modules/ai_assistant/di.js.map +2 -2
- package/dist/modules/ai_assistant/lib/agent-registry.js +1 -1
- package/dist/modules/ai_assistant/lib/agent-registry.js.map +2 -2
- package/dist/modules/ai_assistant/lib/agent-runtime.js +45 -2
- package/dist/modules/ai_assistant/lib/agent-runtime.js.map +2 -2
- package/dist/modules/ai_assistant/lib/agent-tools.js +9 -0
- package/dist/modules/ai_assistant/lib/agent-tools.js.map +2 -2
- package/dist/modules/ai_assistant/lib/generated-registry-loader.js +35 -16
- package/dist/modules/ai_assistant/lib/generated-registry-loader.js.map +2 -2
- package/dist/modules/ai_assistant/lib/llm-adapters/azure.js +74 -0
- package/dist/modules/ai_assistant/lib/llm-adapters/azure.js.map +7 -0
- package/dist/modules/ai_assistant/lib/llm-bootstrap.js +6 -0
- package/dist/modules/ai_assistant/lib/llm-bootstrap.js.map +2 -2
- package/dist/modules/ai_assistant/lib/openai-compatible-presets.js +0 -21
- package/dist/modules/ai_assistant/lib/openai-compatible-presets.js.map +2 -2
- package/dist/modules/ai_assistant/lib/opencode-client.js +8 -2
- package/dist/modules/ai_assistant/lib/opencode-client.js.map +2 -2
- package/dist/modules/ai_assistant/lib/opencode-handlers.js +54 -20
- package/dist/modules/ai_assistant/lib/opencode-handlers.js.map +2 -2
- package/dist/modules/ai_assistant/lib/tool-loader.js +10 -2
- package/dist/modules/ai_assistant/lib/tool-loader.js.map +2 -2
- package/dist/modules/ai_assistant/lib/tool-test-runner.js +3 -1
- package/dist/modules/ai_assistant/lib/tool-test-runner.js.map +2 -2
- package/package.json +7 -6
- package/src/modules/ai_assistant/__tests__/integration/ws-c-tool-pack-coverage.test.ts +10 -0
- package/src/modules/ai_assistant/ai-tools/__tests__/meta-pack.test.ts +10 -0
- package/src/modules/ai_assistant/di.ts +5 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime-object-steps.test.ts +267 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-tools-registry-load.test.ts +90 -0
- package/src/modules/ai_assistant/lib/__tests__/llm-adapters-azure.test.ts +44 -0
- package/src/modules/ai_assistant/lib/__tests__/llm-adapters-openai.test.ts +7 -11
- package/src/modules/ai_assistant/lib/__tests__/prepare-mutation.test.ts +10 -0
- package/src/modules/ai_assistant/lib/__tests__/tool-loader-ensure-loaded.test.ts +51 -0
- package/src/modules/ai_assistant/lib/agent-registry.ts +5 -1
- package/src/modules/ai_assistant/lib/agent-runtime.ts +86 -3
- package/src/modules/ai_assistant/lib/agent-tools.ts +16 -0
- package/src/modules/ai_assistant/lib/generated-registry-loader.ts +59 -16
- package/src/modules/ai_assistant/lib/llm-adapters/azure.ts +120 -0
- package/src/modules/ai_assistant/lib/llm-bootstrap.ts +7 -0
- package/src/modules/ai_assistant/lib/openai-compatible-presets.ts +0 -25
- package/src/modules/ai_assistant/lib/opencode-client.ts +32 -6
- package/src/modules/ai_assistant/lib/opencode-handlers.ts +80 -20
- package/src/modules/ai_assistant/lib/tool-loader.ts +37 -1
- package/src/modules/ai_assistant/lib/tool-test-runner.ts +3 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
[build:ai-assistant] found
|
|
1
|
+
[build:ai-assistant] found 235 entry points
|
|
2
2
|
[build:ai-assistant] built successfully
|
package/AGENTS.md
CHANGED
|
@@ -353,7 +353,7 @@ The unified AI runtime picks the first configured provider from `llmProviderRegi
|
|
|
353
353
|
| `GROQ_API_KEY` | Groq | `llama-3.3-70b-versatile` | `GROQ_BASE_URL` |
|
|
354
354
|
| `TOGETHER_API_KEY` | Together AI | `meta-llama/Llama-3.3-70B-Instruct-Turbo` | `TOGETHER_BASE_URL` |
|
|
355
355
|
| `FIREWORKS_API_KEY` | Fireworks AI | `accounts/fireworks/models/llama-v3p3-70b-instruct` | `FIREWORKS_BASE_URL` |
|
|
356
|
-
| `AZURE_OPENAI_API_KEY` | Azure OpenAI | `gpt-5-mini` | `
|
|
356
|
+
| `AZURE_OPENAI_API_KEY` | Azure OpenAI / Foundry | `gpt-5-mini` | `AZURE_OPENAI_RESOURCE_NAME` (preferred) or `AZURE_OPENAI_BASE_URL` — one is required |
|
|
357
357
|
| `LITELLM_API_KEY` | LiteLLM | `gpt-4o-mini` | `LITELLM_BASE_URL` |
|
|
358
358
|
| `OLLAMA_API_KEY` | Ollama (local) | `llama3.3` | `OLLAMA_BASE_URL` |
|
|
359
359
|
| `OPENROUTER_API_KEY` | OpenRouter | `meta-llama/llama-3.3-70b-instruct` | `OPENROUTER_BASE_URL` |
|
|
@@ -385,6 +385,8 @@ Per-module overrides (Phase 1 of the same spec — agent-default provider + per-
|
|
|
385
385
|
| `OM_AI_<MODULE>_MODEL` | Optional. Per-module model override, uppercased from the agent's `moduleId`. Examples: `OM_AI_CATALOG_MODEL=claude-opus-4-20250514`, `OM_AI_INBOX_OPS_MODEL=gpt-4o`. The legacy `<MODULE>_AI_MODEL` form (e.g. `INBOX_OPS_AI_MODEL`) is read as a backward-compatibility fallback. Accepts a slash-qualified `<provider>/<model>` shorthand. |
|
|
386
386
|
| `OM_AI_<MODULE>_PROVIDER` | Optional. Per-module provider override, uppercased from the agent's `moduleId`. Examples: `OM_AI_CATALOG_PROVIDER=openai`, `OM_AI_INBOX_OPS_PROVIDER=anthropic`. The legacy `<MODULE>_AI_PROVIDER` form (e.g. `INBOX_OPS_AI_PROVIDER`) is read as a backward-compatibility fallback. Provider-only preferences can fall through when unconfigured; paired provider/model overrides fail closed. |
|
|
387
387
|
|
|
388
|
+
Azure is a **native** Responses-API adapter, not an OpenAI-compatible preset: [`.ai/docs/opencode-runtime.md`](../../.ai/docs/opencode-runtime.md).
|
|
389
|
+
|
|
388
390
|
All new callers MUST use `createModelFactory(container)` from `@open-mercato/ai-assistant/modules/ai_assistant/lib/model-factory` — never inline provider SDK calls (`createAnthropic`, `createOpenAI`, `createGoogleGenerativeAI`). The factory enforces the resolution order (caller override → `OM_AI_<MODULE>_MODEL` → `agentDefaultModel` → `OM_AI_MODEL` → provider default) and throws the documented `AiModelFactoryError` codes when misconfigured. See **Model Resolution** below.
|
|
389
391
|
|
|
390
392
|
Operator-defined allowlist (Phase 1780-5) — the ULTIMATE constraint that clips every other source. When set, the settings UI is clipped to the allowed subset, the chat-UI `<ModelPicker>` only offers these values, the dispatcher rejects out-of-allowlist `?provider=` / `?model=` query params with typed 400 errors, and the model-factory swaps to a safe pair (emitting `console.warn` and an `allowlistFallback` field on the resolution) whenever an agent default, tenant override, or higher-priority source resolves to something blocked.
|
|
@@ -773,6 +775,8 @@ interface CommandPaletteContextValue {
|
|
|
773
775
|
|
|
774
776
|
## Running the Stack
|
|
775
777
|
|
|
778
|
+
Dev modes (**hybrid** default): [`.ai/docs/opencode-runtime.md`](../../.ai/docs/opencode-runtime.md).
|
|
779
|
+
|
|
776
780
|
### Choose an MCP Server Mode
|
|
777
781
|
|
|
778
782
|
| Feature | Dev (`mcp:dev`) — when to use | Production (`mcp:serve`) — when to use |
|
|
@@ -830,8 +834,8 @@ yarn mcp:serve
|
|
|
830
834
|
|
|
831
835
|
2. Start OpenCode (Docker):
|
|
832
836
|
```bash
|
|
833
|
-
docker start opencode
|
|
834
|
-
# Or: docker-compose up opencode
|
|
837
|
+
docker start mercato-opencode
|
|
838
|
+
# Or: docker compose --project-directory . -f starters/docker/compose.infra.yml up -d opencode
|
|
835
839
|
```
|
|
836
840
|
|
|
837
841
|
3. Verify connectivity:
|
|
@@ -962,45 +966,9 @@ The module deliberately **does not** call `searchService.bulkIndex(...)` on boot
|
|
|
962
966
|
|
|
963
967
|
## Docker Configuration
|
|
964
968
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
When modifying the Docker setup, follow this structure:
|
|
968
|
-
|
|
969
|
-
```yaml
|
|
970
|
-
# docker-compose.yml
|
|
971
|
-
services:
|
|
972
|
-
opencode:
|
|
973
|
-
build: ./docker/opencode
|
|
974
|
-
container_name: opencode-mvp
|
|
975
|
-
ports:
|
|
976
|
-
- "4096:4096"
|
|
977
|
-
environment:
|
|
978
|
-
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
|
979
|
-
volumes:
|
|
980
|
-
- ./docker/opencode/opencode.json:/root/.opencode/opencode.json
|
|
981
|
-
```
|
|
982
|
-
|
|
983
|
-
MUST keep port 4096 for OpenCode. MUST mount `opencode.json` to `/root/.opencode/opencode.json`.
|
|
984
|
-
|
|
985
|
-
### OpenCode Config
|
|
986
|
-
|
|
987
|
-
```json
|
|
988
|
-
{
|
|
989
|
-
"provider": "anthropic",
|
|
990
|
-
"model": "claude-sonnet-4-20250514",
|
|
991
|
-
"mcp": {
|
|
992
|
-
"open-mercato": {
|
|
993
|
-
"type": "sse",
|
|
994
|
-
"url": "http://host.docker.internal:3001/mcp",
|
|
995
|
-
"headers": {
|
|
996
|
-
"x-api-key": "omk_..."
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
```
|
|
969
|
+
MUST keep port 4096 for OpenCode, MUST mount `opencode.json` at `/root/.opencode/opencode.json`, and MUST use `host.docker.internal` (not `localhost`) for Docker→host calls. File-defined agents generate into `docker/opencode/{agents,skills}/`; after editing any `agents/<id>/` file run `yarn generate` and **restart** OpenCode.
|
|
1002
970
|
|
|
1003
|
-
|
|
971
|
+
Compose structure, the `opencode.json` MCP block, container paths, propose-only: [`.ai/docs/opencode-runtime.md`](../../.ai/docs/opencode-runtime.md).
|
|
1004
972
|
|
|
1005
973
|
## Rules for the Debug Panel
|
|
1006
974
|
|
|
@@ -1491,7 +1459,7 @@ Agents that need multi-step tool loops configure the `loop` block on `AiAgentDef
|
|
|
1491
1459
|
|
|
1492
1460
|
### 2026-08-05 - @app module entries are compiled, not raw TS
|
|
1493
1461
|
|
|
1494
|
-
`compileAndImportGenerated` (`lib/generated-registry-loader.ts`) compiles every `@app` module entry a generated registry references into `<appRoot>/.mercato/generated/app-modules/`, via the new `compileAppSourceFile` in `shared/lib/bootstrap/dynamicLoader` (reuses `loadBootstrapData`'s esbuild bundle and dep cache; packages stay external). The 2026-06-24 fix below only rewrote the specifier to an absolute `.ts` path, which holds
|
|
1462
|
+
`compileAndImportGenerated` (`lib/generated-registry-loader.ts`) compiles every `@app` module entry a generated registry references into `<appRoot>/.mercato/generated/app-modules/`, via the new `compileAppSourceFile` in `shared/lib/bootstrap/dynamicLoader` (reuses `loadBootstrapData`'s esbuild bundle and dep cache; packages stay external). The 2026-06-24 fix below only rewrote the specifier to an absolute `.ts` path, which holds while the target's graph stays inside Node's type stripping — a real module's does not (`./di` is extensionless, `./data/entities` has decorators), so the first app-local `ai-tools.ts` killed the registry. Uncompilable modules log and fall back to the raw path. Additive: the artifact map is an optional third `rewriteGeneratedAliasImports` arg.
|
|
1495
1463
|
|
|
1496
1464
|
### 2026-06-24 - MCP dev server loads ai-tools for @app local modules (#3524)
|
|
1497
1465
|
|
package/README.md
CHANGED
|
@@ -79,12 +79,21 @@ The `x-api-key` should be an Open Mercato API key created in the backend admin (
|
|
|
79
79
|
|
|
80
80
|
### Step 4: Start Services
|
|
81
81
|
|
|
82
|
+
The default hybrid dev mode needs just two commands — `yarn infra:up` starts OpenCode plus the infrastructure containers, and `yarn dev` runs the app **and** the MCP server on the host (provisioning the MCP API key into `.mercato/mcp-shared/mcp-api-key` automatically):
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
yarn infra:up
|
|
86
|
+
yarn dev
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
To run the pieces individually instead:
|
|
90
|
+
|
|
82
91
|
```bash
|
|
83
92
|
# Terminal 1: Start MCP development server
|
|
84
93
|
yarn mcp:dev
|
|
85
94
|
|
|
86
95
|
# Terminal 2: Start OpenCode container
|
|
87
|
-
docker-compose up opencode
|
|
96
|
+
docker compose --project-directory . -f starters/docker/compose.infra.yml up -d opencode
|
|
88
97
|
|
|
89
98
|
# Terminal 3: Start Next.js app
|
|
90
99
|
yarn dev
|
|
@@ -589,7 +598,7 @@ The AI will relay this message naturally to the user.
|
|
|
589
598
|
|
|
590
599
|
## Docker Configuration
|
|
591
600
|
|
|
592
|
-
The OpenCode container is configured via `docker
|
|
601
|
+
The OpenCode container is configured via `starters/docker/compose.infra.yml`. It reads environment variables from your `.env` file:
|
|
593
602
|
|
|
594
603
|
```yaml
|
|
595
604
|
services:
|
|
@@ -609,5 +618,5 @@ services:
|
|
|
609
618
|
|
|
610
619
|
Start with:
|
|
611
620
|
```bash
|
|
612
|
-
docker-compose up opencode
|
|
621
|
+
docker compose --project-directory . -f starters/docker/compose.infra.yml up -d opencode
|
|
613
622
|
```
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { asFunction, asValue } from "awilix";
|
|
2
2
|
import { toolRegistry } from "./lib/tool-registry.js";
|
|
3
3
|
import { createModerationService } from "./lib/moderation.js";
|
|
4
|
+
import { createOpenCodeClient } from "./lib/opencode-client.js";
|
|
4
5
|
function register(container) {
|
|
5
6
|
container.register({
|
|
6
7
|
mcpToolRegistry: asValue(toolRegistry),
|
|
7
8
|
// Input pre-moderation service (OpenAI /v1/moderations). Singleton +
|
|
8
9
|
// overridable by downstream apps via the module overrides DI seam.
|
|
9
|
-
moderationService: asFunction(() => createModerationService()).singleton()
|
|
10
|
+
moderationService: asFunction(() => createModerationService()).singleton(),
|
|
11
|
+
// Injectable OpenCode client so the file-agent runner (and tests, via a
|
|
12
|
+
// fake client) resolve it from DI instead of constructing it inline.
|
|
13
|
+
// Production wiring uses the env-configured factory.
|
|
14
|
+
openCodeClient: asFunction(() => createOpenCodeClient()).singleton()
|
|
10
15
|
});
|
|
11
16
|
}
|
|
12
17
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/ai_assistant/di.ts"],
|
|
4
|
-
"sourcesContent": ["import { asFunction, asValue } from 'awilix'\nimport type { AwilixContainer } from 'awilix'\nimport { toolRegistry } from './lib/tool-registry'\nimport { createModerationService } from './lib/moderation'\n\nexport function register(container: AwilixContainer): void {\n container.register({\n mcpToolRegistry: asValue(toolRegistry),\n // Input pre-moderation service (OpenAI /v1/moderations). Singleton +\n // overridable by downstream apps via the module overrides DI seam.\n moderationService: asFunction(() => createModerationService()).singleton(),\n })\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,YAAY,eAAe;AAEpC,SAAS,oBAAoB;AAC7B,SAAS,+BAA+B;
|
|
4
|
+
"sourcesContent": ["import { asFunction, asValue } from 'awilix'\nimport type { AwilixContainer } from 'awilix'\nimport { toolRegistry } from './lib/tool-registry'\nimport { createModerationService } from './lib/moderation'\nimport { createOpenCodeClient } from './lib/opencode-client'\n\nexport function register(container: AwilixContainer): void {\n container.register({\n mcpToolRegistry: asValue(toolRegistry),\n // Input pre-moderation service (OpenAI /v1/moderations). Singleton +\n // overridable by downstream apps via the module overrides DI seam.\n moderationService: asFunction(() => createModerationService()).singleton(),\n // Injectable OpenCode client so the file-agent runner (and tests, via a\n // fake client) resolve it from DI instead of constructing it inline.\n // Production wiring uses the env-configured factory.\n openCodeClient: asFunction(() => createOpenCodeClient()).singleton(),\n })\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAAY,eAAe;AAEpC,SAAS,oBAAoB;AAC7B,SAAS,+BAA+B;AACxC,SAAS,4BAA4B;AAE9B,SAAS,SAAS,WAAkC;AACzD,YAAU,SAAS;AAAA,IACjB,iBAAiB,QAAQ,YAAY;AAAA;AAAA;AAAA,IAGrC,mBAAmB,WAAW,MAAM,wBAAwB,CAAC,EAAE,UAAU;AAAA;AAAA;AAAA;AAAA,IAIzE,gBAAgB,WAAW,MAAM,qBAAqB,CAAC,EAAE,UAAU;AAAA,EACrE,CAAC;AACH;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -16,7 +16,7 @@ async function importGeneratedAiAgentsModule() {
|
|
|
16
16
|
} catch {
|
|
17
17
|
const tsPath = findGeneratedFile("ai-agents.generated.ts");
|
|
18
18
|
if (!tsPath) return null;
|
|
19
|
-
return compileAndImportGenerated(tsPath);
|
|
19
|
+
return compileAndImportGenerated(tsPath, { bundleLocalModules: true });
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
function isStringArray(value) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/ai_assistant/lib/agent-registry.ts"],
|
|
4
|
-
"sourcesContent": ["import { createLogger } from '@open-mercato/shared/lib/logger'\nimport { llmProviderRegistry } from './llm-registry'\nimport type { AiAgentDefinition, AiAgentExtension, AiAgentSuggestion } from './ai-agent-definition'\nimport {\n applyAgentOverrideMap,\n composeAgentExtensionEntries,\n composeAgentOverrideMap,\n type AiAgentExtensionConfigEntry,\n type AiAgentOverrideConfigEntry,\n} from './ai-overrides'\nimport { TASK_PLAN_TOOL_NAME } from './task-plan-labels'\nimport { findGeneratedFile, compileAndImportGenerated } from './generated-registry-loader'\n\nconst logger = createLogger('ai_assistant')\n\nconst agentsById = new Map<string, AiAgentDefinition>()\nlet loaded = false\n\n/**\n * Import the generated `ai-agents.generated.ts` registry.\n *\n * Dual-strategy, mirroring `tool-loader`'s `importGeneratedAiToolsModule`:\n * 1. Prefer the `@/` path-alias import \u2014 the Next.js bundler resolves it at\n * build time, so the in-app agent runtime is unchanged.\n * 2. Fall back to locating + compiling the file from disk when the alias\n * import throws. That only happens in a standalone Node process (the\n * `mcp:dev` / `mcp:serve` MCP servers), where `@/` is not a real package\n * specifier and Node throws `ERR_MODULE_NOT_FOUND`. Without this the\n * `meta.list_agents` / `meta.describe_agent` tools return an empty agent\n * registry over MCP.\n *\n * Returns `null` when no generated file exists (pre-generate builds, tests).\n */\nasync function importGeneratedAiAgentsModule(): Promise<Record<string, unknown> | null> {\n try {\n return (await import(\n '@/.mercato/generated/ai-agents.generated'\n )) as Record<string, unknown>\n } catch {\n const tsPath = findGeneratedFile('ai-agents.generated.ts')\n if (!tsPath) return null\n return compileAndImportGenerated(tsPath)\n }\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === 'string')\n}\n\nfunction isAiAgentSuggestion(value: unknown): value is AiAgentSuggestion {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Record<string, unknown>\n return typeof candidate.label === 'string' && typeof candidate.prompt === 'string'\n}\n\nfunction isAiAgentExtension(value: unknown): value is AiAgentExtension {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Record<string, unknown>\n return (\n typeof candidate.targetAgentId === 'string' &&\n (!('replaceAllowedTools' in candidate) || isStringArray(candidate.replaceAllowedTools)) &&\n (!('deleteAllowedTools' in candidate) || isStringArray(candidate.deleteAllowedTools)) &&\n (!('appendAllowedTools' in candidate) || isStringArray(candidate.appendAllowedTools)) &&\n (!('taskPlan' in candidate) ||\n (candidate.taskPlan != null &&\n typeof candidate.taskPlan === 'object' &&\n (!('enabled' in candidate.taskPlan) ||\n typeof (candidate.taskPlan as { enabled?: unknown }).enabled === 'boolean'))) &&\n (!('replaceSystemPrompt' in candidate) || typeof candidate.replaceSystemPrompt === 'string') &&\n (!('appendSystemPrompt' in candidate) || typeof candidate.appendSystemPrompt === 'string') &&\n (!('replaceSuggestions' in candidate) ||\n (Array.isArray(candidate.replaceSuggestions) && candidate.replaceSuggestions.every(isAiAgentSuggestion))) &&\n (!('deleteSuggestions' in candidate) || isStringArray(candidate.deleteSuggestions)) &&\n (!('appendSuggestions' in candidate) ||\n (Array.isArray(candidate.appendSuggestions) && candidate.appendSuggestions.every(isAiAgentSuggestion))) &&\n (!('suggestions' in candidate) ||\n (Array.isArray(candidate.suggestions) && candidate.suggestions.every(isAiAgentSuggestion)))\n )\n}\n\nfunction isAiAgentDefinition(value: unknown): value is AiAgentDefinition {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Record<string, unknown>\n return (\n typeof candidate.id === 'string' &&\n typeof candidate.moduleId === 'string' &&\n typeof candidate.label === 'string' &&\n typeof candidate.description === 'string' &&\n typeof candidate.systemPrompt === 'string' &&\n isStringArray(candidate.allowedTools)\n )\n}\n\nfunction uniqueStrings(values: readonly string[]): string[] {\n return Array.from(new Set(values.filter((value) => typeof value === 'string' && value.length > 0)))\n}\n\nexport function isAgentTaskPlanEnabled(agent: Pick<AiAgentDefinition, 'taskPlan'>): boolean {\n return agent.taskPlan?.enabled === true\n}\n\nfunction normalizeTaskPlanTool(agent: AiAgentDefinition): AiAgentDefinition {\n const enabled = isAgentTaskPlanEnabled(agent)\n const withoutInternalTool = agent.allowedTools.filter((toolName) => toolName !== TASK_PLAN_TOOL_NAME)\n const allowedTools = enabled\n ? uniqueStrings([...withoutInternalTool, TASK_PLAN_TOOL_NAME])\n : uniqueStrings(withoutInternalTool)\n if (\n allowedTools.length === agent.allowedTools.length &&\n allowedTools.every((toolName, index) => toolName === agent.allowedTools[index])\n ) {\n return agent\n }\n return {\n ...agent,\n allowedTools,\n }\n}\n\nfunction applyStringListPatch(\n current: readonly string[],\n patch: {\n replace?: readonly string[]\n delete?: readonly string[]\n append?: readonly string[]\n },\n): string[] {\n const deleted = new Set(patch.delete ?? [])\n return uniqueStrings([\n ...(patch.replace ?? current).filter((value) => !deleted.has(value)),\n ...(patch.append ?? []),\n ])\n}\n\nfunction suggestionDeleteKey(suggestion: AiAgentSuggestion): string[] {\n return [suggestion.label, suggestion.prompt].filter((value) => value.length > 0)\n}\n\nfunction applySuggestionPatch(\n current: readonly AiAgentSuggestion[],\n patch: {\n replace?: readonly AiAgentSuggestion[]\n delete?: readonly string[]\n append?: readonly AiAgentSuggestion[]\n },\n): AiAgentSuggestion[] {\n const deleted = new Set(patch.delete ?? [])\n const base = patch.replace ?? current\n const out: AiAgentSuggestion[] = []\n const seen = new Set<string>()\n for (const suggestion of base) {\n if (suggestionDeleteKey(suggestion).some((key) => deleted.has(key))) continue\n const key = `${suggestion.label}\\n${suggestion.prompt}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push(suggestion)\n }\n for (const suggestion of patch.append ?? []) {\n const key = `${suggestion.label}\\n${suggestion.prompt}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push(suggestion)\n }\n return out\n}\n\nfunction validateAndNormalizeAgent(candidate: AiAgentDefinition): AiAgentDefinition {\n const taskPlanNormalized = normalizeTaskPlanTool(candidate)\n const rawProvider = candidate.defaultProvider\n if (typeof rawProvider !== 'string' || rawProvider.trim().length === 0) {\n return taskPlanNormalized\n }\n const providerHint = rawProvider.trim()\n const registered = llmProviderRegistry.get(providerHint)\n if (!registered) {\n logger.warn('Agent declares a defaultProvider not registered in llmProviderRegistry; registering with defaultProvider undefined', {\n agentId: candidate.id,\n defaultProvider: providerHint,\n })\n return { ...taskPlanNormalized, defaultProvider: undefined }\n }\n return taskPlanNormalized\n}\n\nfunction populateFromAgents(agents: unknown[]): void {\n for (const candidate of agents) {\n if (!isAiAgentDefinition(candidate)) {\n logger.warn('AI Agents \u2014 Skipping malformed agent entry in ai-agents.generated.ts')\n continue\n }\n const existing = agentsById.get(candidate.id)\n if (existing) {\n throw new Error(\n `[AI Agents] Duplicate agent id \"${candidate.id}\" \u2014 already registered by module \"${existing.moduleId}\", conflicts with module \"${candidate.moduleId}\". Export \\`aiAgentOverrides\\` from your module's \\`ai-agents.ts\\` (or set it on the modules.ts entry) to replace an agent across modules.`\n )\n }\n agentsById.set(candidate.id, validateAndNormalizeAgent(candidate))\n }\n}\n\nasync function loadOverrideEntries(): Promise<AiAgentOverrideConfigEntry[]> {\n try {\n const mod = (await importGeneratedAiAgentsModule()) as {\n aiAgentOverrideEntries?: unknown[]\n } | null\n return mod && Array.isArray(mod.aiAgentOverrideEntries)\n ? (mod.aiAgentOverrideEntries as AiAgentOverrideConfigEntry[])\n : []\n } catch {\n // No generated file yet \u2014 pre-generate builds and tests fall through.\n return []\n }\n}\n\nfunction applyOverridesToRegistry(entries: readonly AiAgentOverrideConfigEntry[]): void {\n const overrideMap = composeAgentOverrideMap(entries)\n if (Object.keys(overrideMap).length === 0) return\n const overridden = applyAgentOverrideMap(Array.from(agentsById.values()), overrideMap)\n agentsById.clear()\n for (const agent of overridden) agentsById.set(agent.id, agent)\n for (const [id, value] of Object.entries(overrideMap)) {\n const verb = value === null ? 'disabled' : 'replaced'\n logger.info('Agent changed by override', { agentId: id, change: verb })\n }\n}\n\nasync function loadExtensionEntries(): Promise<AiAgentExtension[]> {\n try {\n const mod = (await importGeneratedAiAgentsModule()) as {\n aiAgentExtensionEntries?: unknown[]\n } | null\n const entries =\n mod && Array.isArray(mod.aiAgentExtensionEntries)\n ? (mod.aiAgentExtensionEntries as AiAgentExtensionConfigEntry[])\n : []\n return composeAgentExtensionEntries(entries).filter(isAiAgentExtension)\n } catch {\n return []\n }\n}\n\nfunction applyExtensionsToRegistry(extensions: readonly AiAgentExtension[]): void {\n if (extensions.length === 0) return\n for (const extension of extensions) {\n const agent = agentsById.get(extension.targetAgentId)\n if (!agent) {\n logger.warn('Skipping extension for unknown agent', { agentId: extension.targetAgentId })\n continue\n }\n\n const replacementSystemPrompt = extension.replaceSystemPrompt?.trim()\n const appendSystemPrompt = extension.appendSystemPrompt?.trim()\n const systemPrompt = replacementSystemPrompt ?? agent.systemPrompt.trim()\n const patchedAgent: AiAgentDefinition = {\n ...agent,\n allowedTools: applyStringListPatch(agent.allowedTools, {\n replace: extension.replaceAllowedTools,\n delete: extension.deleteAllowedTools,\n append: extension.appendAllowedTools,\n }),\n taskPlan: extension.taskPlan !== undefined ? extension.taskPlan : agent.taskPlan,\n systemPrompt: appendSystemPrompt\n ? `${systemPrompt}\\n\\n${appendSystemPrompt}`\n : systemPrompt,\n suggestions: applySuggestionPatch(agent.suggestions ?? [], {\n replace: extension.replaceSuggestions,\n delete: extension.deleteSuggestions,\n append: [\n ...(extension.appendSuggestions ?? []),\n ...(extension.suggestions ?? []),\n ],\n }),\n }\n agentsById.set(agent.id, validateAndNormalizeAgent(patchedAgent))\n }\n}\n\nexport async function loadAgentRegistry(): Promise<void> {\n if (loaded) return\n try {\n const mod = (await importGeneratedAiAgentsModule()) as {\n allAiAgents?: unknown[]\n } | null\n const agents = mod && Array.isArray(mod.allAiAgents) ? mod.allAiAgents : []\n populateFromAgents(agents)\n } catch (error) {\n logger.error('AI Agents \u2014 Could not load ai-agents.generated.ts (agent registry empty)', { err: error })\n } finally {\n try {\n const overrideEntries = await loadOverrideEntries()\n applyOverridesToRegistry(overrideEntries)\n const extensionEntries = await loadExtensionEntries()\n applyExtensionsToRegistry(extensionEntries)\n } catch (error) {\n logger.error('AI Agents \u2014 Failed to apply agent overrides/extensions', { err: error })\n }\n loaded = true\n }\n}\n\nexport function getAgent(id: string): AiAgentDefinition | undefined {\n return agentsById.get(id)\n}\n\nexport function listAgents(): AiAgentDefinition[] {\n return Array.from(agentsById.values()).sort((a, b) => a.id.localeCompare(b.id))\n}\n\nexport function listAgentsByModule(moduleId: string): AiAgentDefinition[] {\n return listAgents().filter((agent) => agent.moduleId === moduleId)\n}\n\n/**\n * @__internal\n * Test-only hook \u2014 clears the cached registry so `loadAgentRegistry` re-evaluates its source.\n */\nexport function resetAgentRegistryForTests(): void {\n agentsById.clear()\n loaded = false\n}\n\n/**\n * @__internal\n * Test-only hook \u2014 seeds the registry directly from a fixture array without going through\n * the dynamic generated-file import. Used by the registry's own unit tests.\n */\nexport function seedAgentRegistryForTests(agents: unknown[]): void {\n agentsById.clear()\n populateFromAgents(agents)\n loaded = true\n}\n\n/**\n * @__internal\n * Test-only hook \u2014 apply override entries against the seeded registry to\n * exercise the override pipeline without round-tripping through the\n * generated file.\n */\nexport function applyAgentOverrideEntriesForTests(\n entries: readonly AiAgentOverrideConfigEntry[],\n): void {\n applyOverridesToRegistry(entries)\n}\n\n/**\n * @__internal Test-only hook \u2014 apply additive extension entries against the\n * seeded registry without round-tripping through the generated file.\n */\nexport function applyAgentExtensionEntriesForTests(\n entries: readonly AiAgentExtension[],\n): void {\n applyExtensionsToRegistry(entries)\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/// <reference path=\"./ai-agents-generated.d.ts\" />\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { llmProviderRegistry } from './llm-registry'\nimport type { AiAgentDefinition, AiAgentExtension, AiAgentSuggestion } from './ai-agent-definition'\nimport {\n applyAgentOverrideMap,\n composeAgentExtensionEntries,\n composeAgentOverrideMap,\n type AiAgentExtensionConfigEntry,\n type AiAgentOverrideConfigEntry,\n} from './ai-overrides'\nimport { TASK_PLAN_TOOL_NAME } from './task-plan-labels'\nimport { findGeneratedFile, compileAndImportGenerated } from './generated-registry-loader'\n\nconst logger = createLogger('ai_assistant')\n\nconst agentsById = new Map<string, AiAgentDefinition>()\nlet loaded = false\n\n/**\n * Import the generated `ai-agents.generated.ts` registry.\n *\n * Dual-strategy, mirroring `tool-loader`'s `importGeneratedAiToolsModule`:\n * 1. Prefer the `@/` path-alias import \u2014 the Next.js bundler resolves it at\n * build time, so the in-app agent runtime is unchanged.\n * 2. Fall back to locating + compiling the file from disk when the alias\n * import throws. That only happens in a standalone Node process (the\n * `mcp:dev` / `mcp:serve` MCP servers), where `@/` is not a real package\n * specifier and Node throws `ERR_MODULE_NOT_FOUND`. Without this the\n * `meta.list_agents` / `meta.describe_agent` tools return an empty agent\n * registry over MCP.\n *\n * Returns `null` when no generated file exists (pre-generate builds, tests).\n */\nasync function importGeneratedAiAgentsModule(): Promise<Record<string, unknown> | null> {\n try {\n return (await import(\n '@/.mercato/generated/ai-agents.generated'\n )) as Record<string, unknown>\n } catch {\n const tsPath = findGeneratedFile('ai-agents.generated.ts')\n if (!tsPath) return null\n // App-source modules (apps/<app>/src/modules/*/ai-agents.ts) are .ts that\n // node cannot import directly \u2014 bundle their sources so one app-source agent\n // does not abort the whole registry.\n return compileAndImportGenerated(tsPath, { bundleLocalModules: true })\n }\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === 'string')\n}\n\nfunction isAiAgentSuggestion(value: unknown): value is AiAgentSuggestion {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Record<string, unknown>\n return typeof candidate.label === 'string' && typeof candidate.prompt === 'string'\n}\n\nfunction isAiAgentExtension(value: unknown): value is AiAgentExtension {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Record<string, unknown>\n return (\n typeof candidate.targetAgentId === 'string' &&\n (!('replaceAllowedTools' in candidate) || isStringArray(candidate.replaceAllowedTools)) &&\n (!('deleteAllowedTools' in candidate) || isStringArray(candidate.deleteAllowedTools)) &&\n (!('appendAllowedTools' in candidate) || isStringArray(candidate.appendAllowedTools)) &&\n (!('taskPlan' in candidate) ||\n (candidate.taskPlan != null &&\n typeof candidate.taskPlan === 'object' &&\n (!('enabled' in candidate.taskPlan) ||\n typeof (candidate.taskPlan as { enabled?: unknown }).enabled === 'boolean'))) &&\n (!('replaceSystemPrompt' in candidate) || typeof candidate.replaceSystemPrompt === 'string') &&\n (!('appendSystemPrompt' in candidate) || typeof candidate.appendSystemPrompt === 'string') &&\n (!('replaceSuggestions' in candidate) ||\n (Array.isArray(candidate.replaceSuggestions) && candidate.replaceSuggestions.every(isAiAgentSuggestion))) &&\n (!('deleteSuggestions' in candidate) || isStringArray(candidate.deleteSuggestions)) &&\n (!('appendSuggestions' in candidate) ||\n (Array.isArray(candidate.appendSuggestions) && candidate.appendSuggestions.every(isAiAgentSuggestion))) &&\n (!('suggestions' in candidate) ||\n (Array.isArray(candidate.suggestions) && candidate.suggestions.every(isAiAgentSuggestion)))\n )\n}\n\nfunction isAiAgentDefinition(value: unknown): value is AiAgentDefinition {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Record<string, unknown>\n return (\n typeof candidate.id === 'string' &&\n typeof candidate.moduleId === 'string' &&\n typeof candidate.label === 'string' &&\n typeof candidate.description === 'string' &&\n typeof candidate.systemPrompt === 'string' &&\n isStringArray(candidate.allowedTools)\n )\n}\n\nfunction uniqueStrings(values: readonly string[]): string[] {\n return Array.from(new Set(values.filter((value) => typeof value === 'string' && value.length > 0)))\n}\n\nexport function isAgentTaskPlanEnabled(agent: Pick<AiAgentDefinition, 'taskPlan'>): boolean {\n return agent.taskPlan?.enabled === true\n}\n\nfunction normalizeTaskPlanTool(agent: AiAgentDefinition): AiAgentDefinition {\n const enabled = isAgentTaskPlanEnabled(agent)\n const withoutInternalTool = agent.allowedTools.filter((toolName) => toolName !== TASK_PLAN_TOOL_NAME)\n const allowedTools = enabled\n ? uniqueStrings([...withoutInternalTool, TASK_PLAN_TOOL_NAME])\n : uniqueStrings(withoutInternalTool)\n if (\n allowedTools.length === agent.allowedTools.length &&\n allowedTools.every((toolName, index) => toolName === agent.allowedTools[index])\n ) {\n return agent\n }\n return {\n ...agent,\n allowedTools,\n }\n}\n\nfunction applyStringListPatch(\n current: readonly string[],\n patch: {\n replace?: readonly string[]\n delete?: readonly string[]\n append?: readonly string[]\n },\n): string[] {\n const deleted = new Set(patch.delete ?? [])\n return uniqueStrings([\n ...(patch.replace ?? current).filter((value) => !deleted.has(value)),\n ...(patch.append ?? []),\n ])\n}\n\nfunction suggestionDeleteKey(suggestion: AiAgentSuggestion): string[] {\n return [suggestion.label, suggestion.prompt].filter((value) => value.length > 0)\n}\n\nfunction applySuggestionPatch(\n current: readonly AiAgentSuggestion[],\n patch: {\n replace?: readonly AiAgentSuggestion[]\n delete?: readonly string[]\n append?: readonly AiAgentSuggestion[]\n },\n): AiAgentSuggestion[] {\n const deleted = new Set(patch.delete ?? [])\n const base = patch.replace ?? current\n const out: AiAgentSuggestion[] = []\n const seen = new Set<string>()\n for (const suggestion of base) {\n if (suggestionDeleteKey(suggestion).some((key) => deleted.has(key))) continue\n const key = `${suggestion.label}\\n${suggestion.prompt}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push(suggestion)\n }\n for (const suggestion of patch.append ?? []) {\n const key = `${suggestion.label}\\n${suggestion.prompt}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push(suggestion)\n }\n return out\n}\n\nfunction validateAndNormalizeAgent(candidate: AiAgentDefinition): AiAgentDefinition {\n const taskPlanNormalized = normalizeTaskPlanTool(candidate)\n const rawProvider = candidate.defaultProvider\n if (typeof rawProvider !== 'string' || rawProvider.trim().length === 0) {\n return taskPlanNormalized\n }\n const providerHint = rawProvider.trim()\n const registered = llmProviderRegistry.get(providerHint)\n if (!registered) {\n logger.warn('Agent declares a defaultProvider not registered in llmProviderRegistry; registering with defaultProvider undefined', {\n agentId: candidate.id,\n defaultProvider: providerHint,\n })\n return { ...taskPlanNormalized, defaultProvider: undefined }\n }\n return taskPlanNormalized\n}\n\nfunction populateFromAgents(agents: unknown[]): void {\n for (const candidate of agents) {\n if (!isAiAgentDefinition(candidate)) {\n logger.warn('AI Agents \u2014 Skipping malformed agent entry in ai-agents.generated.ts')\n continue\n }\n const existing = agentsById.get(candidate.id)\n if (existing) {\n throw new Error(\n `[AI Agents] Duplicate agent id \"${candidate.id}\" \u2014 already registered by module \"${existing.moduleId}\", conflicts with module \"${candidate.moduleId}\". Export \\`aiAgentOverrides\\` from your module's \\`ai-agents.ts\\` (or set it on the modules.ts entry) to replace an agent across modules.`\n )\n }\n agentsById.set(candidate.id, validateAndNormalizeAgent(candidate))\n }\n}\n\nasync function loadOverrideEntries(): Promise<AiAgentOverrideConfigEntry[]> {\n try {\n const mod = (await importGeneratedAiAgentsModule()) as {\n aiAgentOverrideEntries?: unknown[]\n } | null\n return mod && Array.isArray(mod.aiAgentOverrideEntries)\n ? (mod.aiAgentOverrideEntries as AiAgentOverrideConfigEntry[])\n : []\n } catch {\n // No generated file yet \u2014 pre-generate builds and tests fall through.\n return []\n }\n}\n\nfunction applyOverridesToRegistry(entries: readonly AiAgentOverrideConfigEntry[]): void {\n const overrideMap = composeAgentOverrideMap(entries)\n if (Object.keys(overrideMap).length === 0) return\n const overridden = applyAgentOverrideMap(Array.from(agentsById.values()), overrideMap)\n agentsById.clear()\n for (const agent of overridden) agentsById.set(agent.id, agent)\n for (const [id, value] of Object.entries(overrideMap)) {\n const verb = value === null ? 'disabled' : 'replaced'\n logger.info('Agent changed by override', { agentId: id, change: verb })\n }\n}\n\nasync function loadExtensionEntries(): Promise<AiAgentExtension[]> {\n try {\n const mod = (await importGeneratedAiAgentsModule()) as {\n aiAgentExtensionEntries?: unknown[]\n } | null\n const entries =\n mod && Array.isArray(mod.aiAgentExtensionEntries)\n ? (mod.aiAgentExtensionEntries as AiAgentExtensionConfigEntry[])\n : []\n return composeAgentExtensionEntries(entries).filter(isAiAgentExtension)\n } catch {\n return []\n }\n}\n\nfunction applyExtensionsToRegistry(extensions: readonly AiAgentExtension[]): void {\n if (extensions.length === 0) return\n for (const extension of extensions) {\n const agent = agentsById.get(extension.targetAgentId)\n if (!agent) {\n logger.warn('Skipping extension for unknown agent', { agentId: extension.targetAgentId })\n continue\n }\n\n const replacementSystemPrompt = extension.replaceSystemPrompt?.trim()\n const appendSystemPrompt = extension.appendSystemPrompt?.trim()\n const systemPrompt = replacementSystemPrompt ?? agent.systemPrompt.trim()\n const patchedAgent: AiAgentDefinition = {\n ...agent,\n allowedTools: applyStringListPatch(agent.allowedTools, {\n replace: extension.replaceAllowedTools,\n delete: extension.deleteAllowedTools,\n append: extension.appendAllowedTools,\n }),\n taskPlan: extension.taskPlan !== undefined ? extension.taskPlan : agent.taskPlan,\n systemPrompt: appendSystemPrompt\n ? `${systemPrompt}\\n\\n${appendSystemPrompt}`\n : systemPrompt,\n suggestions: applySuggestionPatch(agent.suggestions ?? [], {\n replace: extension.replaceSuggestions,\n delete: extension.deleteSuggestions,\n append: [\n ...(extension.appendSuggestions ?? []),\n ...(extension.suggestions ?? []),\n ],\n }),\n }\n agentsById.set(agent.id, validateAndNormalizeAgent(patchedAgent))\n }\n}\n\nexport async function loadAgentRegistry(): Promise<void> {\n if (loaded) return\n try {\n const mod = (await importGeneratedAiAgentsModule()) as {\n allAiAgents?: unknown[]\n } | null\n const agents = mod && Array.isArray(mod.allAiAgents) ? mod.allAiAgents : []\n populateFromAgents(agents)\n } catch (error) {\n logger.error('AI Agents \u2014 Could not load ai-agents.generated.ts (agent registry empty)', { err: error })\n } finally {\n try {\n const overrideEntries = await loadOverrideEntries()\n applyOverridesToRegistry(overrideEntries)\n const extensionEntries = await loadExtensionEntries()\n applyExtensionsToRegistry(extensionEntries)\n } catch (error) {\n logger.error('AI Agents \u2014 Failed to apply agent overrides/extensions', { err: error })\n }\n loaded = true\n }\n}\n\nexport function getAgent(id: string): AiAgentDefinition | undefined {\n return agentsById.get(id)\n}\n\nexport function listAgents(): AiAgentDefinition[] {\n return Array.from(agentsById.values()).sort((a, b) => a.id.localeCompare(b.id))\n}\n\nexport function listAgentsByModule(moduleId: string): AiAgentDefinition[] {\n return listAgents().filter((agent) => agent.moduleId === moduleId)\n}\n\n/**\n * @__internal\n * Test-only hook \u2014 clears the cached registry so `loadAgentRegistry` re-evaluates its source.\n */\nexport function resetAgentRegistryForTests(): void {\n agentsById.clear()\n loaded = false\n}\n\n/**\n * @__internal\n * Test-only hook \u2014 seeds the registry directly from a fixture array without going through\n * the dynamic generated-file import. Used by the registry's own unit tests.\n */\nexport function seedAgentRegistryForTests(agents: unknown[]): void {\n agentsById.clear()\n populateFromAgents(agents)\n loaded = true\n}\n\n/**\n * @__internal\n * Test-only hook \u2014 apply override entries against the seeded registry to\n * exercise the override pipeline without round-tripping through the\n * generated file.\n */\nexport function applyAgentOverrideEntriesForTests(\n entries: readonly AiAgentOverrideConfigEntry[],\n): void {\n applyOverridesToRegistry(entries)\n}\n\n/**\n * @__internal Test-only hook \u2014 apply additive extension entries against the\n * seeded registry without round-tripping through the generated file.\n */\nexport function applyAgentExtensionEntriesForTests(\n entries: readonly AiAgentExtension[],\n): void {\n applyExtensionsToRegistry(entries)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["key"]
|
|
7
7
|
}
|
|
@@ -4,7 +4,9 @@ import { createContainer } from "awilix";
|
|
|
4
4
|
import {
|
|
5
5
|
convertToModelMessages,
|
|
6
6
|
generateObject,
|
|
7
|
+
generateText,
|
|
7
8
|
hasToolCall,
|
|
9
|
+
Output,
|
|
8
10
|
stepCountIs,
|
|
9
11
|
streamObject,
|
|
10
12
|
streamText,
|
|
@@ -1162,16 +1164,57 @@ async function runAiAgentObject(input) {
|
|
|
1162
1164
|
resolvedAttachments
|
|
1163
1165
|
);
|
|
1164
1166
|
const modelMessages = await convertToModelMessages(hydratedMessages);
|
|
1165
|
-
void tools;
|
|
1166
1167
|
const effectiveObjectSessionId = input.sessionId ?? randomUUID();
|
|
1167
1168
|
const objectTurnId = randomUUID();
|
|
1168
1169
|
void effectiveObjectSessionId;
|
|
1169
1170
|
void objectTurnId;
|
|
1171
|
+
const abortController = new AbortController();
|
|
1172
|
+
const resolvedToolNames = Object.keys(tools ?? {});
|
|
1173
|
+
if (input.enableTools && resolvedToolNames.length === 0) {
|
|
1174
|
+
logger.warn(
|
|
1175
|
+
"Agent requested a tool loop but no tools resolved; falling back to a single-shot generateObject",
|
|
1176
|
+
{
|
|
1177
|
+
agentId: agent.id,
|
|
1178
|
+
allowedTools: agent.allowedTools
|
|
1179
|
+
}
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
if (input.enableTools && resolvedToolNames.length > 0) {
|
|
1183
|
+
const toolLoopConfig = resolveEffectiveLoopConfig(agent, input.loop, WRAPPER_DEFAULT_LOOP_CHAT);
|
|
1184
|
+
const toolStopConditions = translateStopConditions(toolLoopConfig, sanitizeToolNameForModel);
|
|
1185
|
+
const toolWrapperPrepareStep = buildWrapperPrepareStep(agent, toolLoopConfig, tools);
|
|
1186
|
+
const toolLoopTrace = buildLoopTraceCollector(
|
|
1187
|
+
agent.id,
|
|
1188
|
+
effectiveObjectSessionId,
|
|
1189
|
+
objectTurnId,
|
|
1190
|
+
toolLoopConfig.onStepFinish
|
|
1191
|
+
);
|
|
1192
|
+
const toolResult = await generateText({
|
|
1193
|
+
model,
|
|
1194
|
+
system: systemPrompt,
|
|
1195
|
+
messages: modelMessages,
|
|
1196
|
+
tools,
|
|
1197
|
+
stopWhen: toolStopConditions,
|
|
1198
|
+
prepareStep: toolWrapperPrepareStep,
|
|
1199
|
+
onStepFinish: toolLoopTrace.onStepFinish,
|
|
1200
|
+
output: Output.object({ schema: resolvedOutput.schema }),
|
|
1201
|
+
abortSignal: abortController.signal
|
|
1202
|
+
});
|
|
1203
|
+
return {
|
|
1204
|
+
mode: "generate",
|
|
1205
|
+
object: toolResult.output,
|
|
1206
|
+
finishReason: toolResult.finishReason,
|
|
1207
|
+
usage: {
|
|
1208
|
+
inputTokens: toolResult.usage?.inputTokens,
|
|
1209
|
+
outputTokens: toolResult.usage?.outputTokens
|
|
1210
|
+
},
|
|
1211
|
+
steps: toolLoopTrace.finalize(null).steps
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1170
1214
|
if (input.loop) {
|
|
1171
1215
|
assertLoopObjectModeCompatible(input.loop);
|
|
1172
1216
|
}
|
|
1173
1217
|
const effectiveLoop = resolveEffectiveLoopConfig(agent, input.loop, WRAPPER_DEFAULT_LOOP_OBJECT);
|
|
1174
|
-
const abortController = new AbortController();
|
|
1175
1218
|
const preparedObjectOptions = {
|
|
1176
1219
|
model,
|
|
1177
1220
|
system: systemPrompt,
|