spine-framework 0.3.124 → 0.3.126
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/.framework/cli/commands/agents.ts +35 -37
- package/.framework/cli/commands/auth.ts +35 -34
- package/.framework/cli/commands/create-app.ts +104 -40
- package/.framework/cli/commands/dev.ts +104 -69
- package/.framework/cli/commands/doctor.ts +179 -69
- package/.framework/cli/commands/generate.ts +83 -143
- package/.framework/cli/commands/init.ts +72 -34
- package/.framework/cli/commands/install-app.ts +260 -125
- package/.framework/cli/commands/items.ts +33 -44
- package/.framework/cli/commands/migrate.ts +122 -40
- package/.framework/cli/commands/migrations.ts +33 -40
- package/.framework/cli/commands/pipelines.ts +31 -39
- package/.framework/cli/commands/status.ts +47 -23
- package/.framework/cli/commands/system.ts +47 -37
- package/.framework/cli/commands/test.ts +112 -68
- package/.framework/cli/commands/validate-app.ts +215 -121
- package/.framework/cli/context.ts +112 -130
- package/.framework/cli/env-loader.ts +47 -18
- package/.framework/cli/index.ts +81 -42
- package/.framework/docs/runtime-types.md +1024 -0
- package/.framework/functions/_shared/agent-runner.ts +1296 -961
- package/.framework/functions/_shared/app-manifest.ts +82 -68
- package/.framework/functions/_shared/audit.ts +135 -83
- package/.framework/functions/_shared/{cil-state.ts → cil.ts} +158 -56
- package/.framework/functions/_shared/db.ts +80 -126
- package/.framework/functions/_shared/hash-api-key.ts +63 -0
- package/.framework/functions/_shared/index.ts +224 -335
- package/.framework/functions/_shared/kb.ts +319 -0
- package/.framework/functions/_shared/llm.ts +92 -0
- package/.framework/functions/_shared/logging.ts +288 -0
- package/.framework/functions/_shared/middleware.ts +329 -365
- package/.framework/functions/_shared/outcomes.ts +36 -0
- package/.framework/functions/_shared/permissions.ts +359 -643
- package/.framework/functions/_shared/pipeline-runner.ts +243 -316
- package/.framework/functions/_shared/principal.ts +376 -468
- package/.framework/functions/_shared/resolve-credentials.ts +102 -0
- package/.framework/functions/_shared/resolve-ids.ts +188 -16
- package/.framework/functions/_shared/retry-deliveries.ts +104 -0
- package/.framework/functions/_shared/schema-utils.ts +611 -373
- package/.framework/functions/_shared/testing.ts +207 -137
- package/.framework/functions/_shared/tracing.ts +276 -0
- package/.framework/functions/_shared/trigger-engine.ts +159 -211
- package/.framework/functions/_shared/webhook-registration.ts +95 -81
- package/.framework/functions/_shared/webhook-registry.ts +153 -75
- package/.framework/functions/account-nodes.ts +31 -53
- package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
- package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
- package/.framework/functions/adapters/openai-outbound.ts +40 -0
- package/.framework/functions/admin-data.ts +685 -537
- package/.framework/functions/admin-funnel.ts +31 -20
- package/.framework/functions/admin-install-funnel.ts +47 -28
- package/.framework/functions/admin-signals.ts +47 -29
- package/.framework/functions/ai-agents.ts +122 -147
- package/.framework/functions/ai-interactions.ts +180 -90
- package/.framework/functions/api-keys.ts +250 -192
- package/.framework/functions/apps.ts +203 -197
- package/.framework/functions/auth.ts +98 -91
- package/.framework/functions/cil-agent-ingest.ts +773 -0
- package/.framework/functions/cil-agent.ts +592 -0
- package/.framework/functions/cil-callmap.ts +1227 -0
- package/.framework/functions/cil-config.ts +583 -0
- package/.framework/functions/cil-context.ts +445 -0
- package/.framework/functions/cil-doc.ts +1752 -0
- package/.framework/functions/cil-embeddings.ts +1202 -0
- package/.framework/functions/cil-graph.ts +585 -0
- package/.framework/functions/cil-ingest.ts +2416 -0
- package/.framework/functions/cil-pipeline.ts +318 -0
- package/.framework/functions/cil-project.ts +499 -0
- package/.framework/functions/cil-validate.ts +469 -0
- package/.framework/functions/debug-auth.ts +73 -53
- package/.framework/functions/deliveries.ts +117 -0
- package/.framework/functions/embeddings.ts +528 -516
- package/.framework/functions/eval-harness.ts +158 -106
- package/.framework/functions/integration-outbound.ts +311 -0
- package/.framework/functions/integration-routes.ts +240 -466
- package/.framework/functions/integrations.ts +132 -140
- package/.framework/functions/invites.ts +401 -201
- package/.framework/functions/item-progress.ts +113 -53
- package/.framework/functions/kb.ts +28 -0
- package/.framework/functions/logs.ts +239 -222
- package/.framework/functions/observability.ts +308 -259
- package/.framework/functions/pipeline-executions.ts +179 -235
- package/.framework/functions/pipelines.ts +163 -205
- package/.framework/functions/prompt-configs.ts +109 -156
- package/.framework/functions/roles.ts +97 -147
- package/.framework/functions/system-cron.ts +132 -218
- package/.framework/functions/system.ts +112 -52
- package/.framework/functions/tests.ts +32 -28
- package/.framework/functions/timers.ts +162 -210
- package/.framework/functions/triggers.ts +282 -346
- package/.framework/functions/types.ts +283 -400
- package/.framework/migrations/001_core_schema.sql +630 -0
- package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
- package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
- package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
- package/.framework/migrations/005_core_observability.sql +598 -0
- package/.framework/migrations/006_security_hardening_rls.sql +97 -0
- package/.framework/migrations/007_kb_article_list.sql +237 -0
- package/.framework/migrations/008_items_target.sql +8 -0
- package/.framework/migrations/009_roles_slug_unique.sql +11 -0
- package/.framework/public/spine.config.json +91 -0
- package/.framework/scripts/app-install-cli.ts +112 -98
- package/.framework/src/App.tsx +67 -20
- package/.framework/src/apps/admin/index.tsx +97 -27
- package/.framework/src/components/AppWrapper.tsx +50 -11
- package/.framework/src/components/CustomAppLoader.tsx +131 -22
- package/.framework/src/components/admin/AdminListPage.tsx +75 -8
- package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
- package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
- package/.framework/src/components/admin/PageInstructions.tsx +49 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
- package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
- package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
- package/.framework/src/components/app-sidebar.tsx +50 -3
- package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
- package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
- package/.framework/src/components/kb/KBArticleList.tsx +557 -0
- package/.framework/src/components/layout/AppShell.tsx +54 -16
- package/.framework/src/components/layout/Header.tsx +50 -38
- package/.framework/src/components/layout/Layout.tsx +48 -32
- package/.framework/src/components/layout/Sidebar.tsx +167 -16
- package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
- package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
- package/.framework/src/components/runtime/DataFilters.tsx +78 -1
- package/.framework/src/components/runtime/DataHeader.tsx +32 -29
- package/.framework/src/components/runtime/DataListPage.tsx +161 -2
- package/.framework/src/components/runtime/DataStats.tsx +48 -1
- package/.framework/src/components/runtime/DataTable.tsx +153 -1
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
- package/.framework/src/components/runtime/index.ts +16 -9
- package/.framework/src/components/search-form.tsx +33 -1
- package/.framework/src/components/shared/AgentView.tsx +48 -51
- package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
- package/.framework/src/components/shared/InviteManager.tsx +113 -52
- package/.framework/src/components/shared/MemberList.tsx +182 -0
- package/.framework/src/components/shared/SchemaFields.tsx +96 -78
- package/.framework/src/components/ui/DataTable.tsx +86 -56
- package/.framework/src/components/ui/Form.tsx +188 -109
- package/.framework/src/components/ui/ItemCard.tsx +86 -58
- package/.framework/src/components/ui/ItemListView.tsx +88 -61
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
- package/.framework/src/components/ui/Modal.tsx +81 -4
- package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
- package/.framework/src/components/ui/accordion.tsx +97 -5
- package/.framework/src/components/ui/alert-dialog.tsx +225 -13
- package/.framework/src/components/ui/alert.tsx +113 -6
- package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
- package/.framework/src/components/ui/avatar.tsx +129 -7
- package/.framework/src/components/ui/badge.tsx +65 -3
- package/.framework/src/components/ui/breadcrumb.tsx +145 -8
- package/.framework/src/components/ui/button-group.tsx +81 -4
- package/.framework/src/components/ui/button.tsx +49 -2
- package/.framework/src/components/ui/calendar.tsx +65 -3
- package/.framework/src/components/ui/card.tsx +145 -8
- package/.framework/src/components/ui/carousel.tsx +129 -7
- package/.framework/src/components/ui/chart.tsx +161 -10
- package/.framework/src/components/ui/checkbox.tsx +49 -2
- package/.framework/src/components/ui/collapsible.tsx +81 -5
- package/.framework/src/components/ui/combobox.tsx +305 -20
- package/.framework/src/components/ui/command.tsx +177 -11
- package/.framework/src/components/ui/context-menu.tsx +273 -17
- package/.framework/src/components/ui/dialog.tsx +209 -27
- package/.framework/src/components/ui/direction.tsx +64 -4
- package/.framework/src/components/ui/drawer.tsx +193 -12
- package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
- package/.framework/src/components/ui/empty.tsx +145 -9
- package/.framework/src/components/ui/field.tsx +209 -14
- package/.framework/src/components/ui/hover-card.tsx +81 -4
- package/.framework/src/components/ui/input-group.tsx +162 -10
- package/.framework/src/components/ui/input-otp.tsx +98 -5
- package/.framework/src/components/ui/input.tsx +49 -2
- package/.framework/src/components/ui/item.tsx +225 -13
- package/.framework/src/components/ui/kbd.tsx +65 -3
- package/.framework/src/components/ui/label.tsx +49 -2
- package/.framework/src/components/ui/menubar.tsx +289 -17
- package/.framework/src/components/ui/native-select.tsx +97 -5
- package/.framework/src/components/ui/navigation-menu.tsx +177 -10
- package/.framework/src/components/ui/pagination.tsx +161 -9
- package/.framework/src/components/ui/popover.tsx +145 -8
- package/.framework/src/components/ui/progress.tsx +49 -2
- package/.framework/src/components/ui/radio-group.tsx +65 -3
- package/.framework/src/components/ui/resizable.tsx +82 -4
- package/.framework/src/components/ui/scroll-area.tsx +65 -3
- package/.framework/src/components/ui/select.tsx +193 -11
- package/.framework/src/components/ui/separator.tsx +49 -2
- package/.framework/src/components/ui/sheet.tsx +193 -11
- package/.framework/src/components/ui/sidebar.tsx +481 -38
- package/.framework/src/components/ui/skeleton.tsx +49 -2
- package/.framework/src/components/ui/slider.tsx +49 -2
- package/.framework/src/components/ui/sonner.tsx +49 -2
- package/.framework/src/components/ui/spinner.tsx +49 -2
- package/.framework/src/components/ui/switch.tsx +49 -2
- package/.framework/src/components/ui/table-primitives.tsx +160 -33
- package/.framework/src/components/ui/table.tsx +161 -9
- package/.framework/src/components/ui/tabs.tsx +113 -6
- package/.framework/src/components/ui/textarea.tsx +49 -2
- package/.framework/src/components/ui/toggle-group.tsx +81 -4
- package/.framework/src/components/ui/toggle.tsx +65 -3
- package/.framework/src/components/ui/tooltip.tsx +97 -5
- package/.framework/src/contexts/AppContext.tsx +129 -45
- package/.framework/src/contexts/AuthContext.tsx +187 -144
- package/.framework/src/hooks/use-mobile.ts +48 -2
- package/.framework/src/hooks/useApi.ts +208 -159
- package/.framework/src/hooks/useAppPath.ts +32 -23
- package/.framework/src/hooks/useApps.ts +51 -20
- package/.framework/src/hooks/useEntityList.ts +32 -78
- package/.framework/src/hooks/useEntityRecord.ts +35 -96
- package/.framework/src/hooks/useForm.ts +32 -92
- package/.framework/src/hooks/useListSchema.ts +49 -78
- package/.framework/src/hooks/useSchemaRecord.ts +114 -105
- package/.framework/src/lib/api.ts +195 -116
- package/.framework/src/lib/supabase.ts +98 -64
- package/.framework/src/lib/utils.ts +287 -151
- package/.framework/src/main.tsx +49 -3
- package/.framework/src/pages/DashboardPage.tsx +52 -29
- package/.framework/src/pages/NotFoundPage.tsx +32 -10
- package/.framework/src/pages/SetupPage.tsx +32 -12
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
- package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
- package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
- package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
- package/.framework/src/pages/admin/AppsPage.tsx +49 -2
- package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
- package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
- package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
- package/.framework/src/pages/admin/LogsPage.tsx +422 -75
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
- package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
- package/.framework/src/pages/admin/RolesPage.tsx +171 -18
- package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
- package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
- package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
- package/.framework/src/pages/admin/TimersPage.tsx +48 -20
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
- package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
- package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
- package/.framework/src/pages/admin/TypesPage.tsx +48 -20
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
- package/.framework/src/pages/auth/LoginPage.tsx +50 -23
- package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
- package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
- package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
- package/.framework/src/types/auth.ts +128 -71
- package/.framework/src/types/types.ts +373 -157
- package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
- package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
- package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
- package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
- package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
- package/docs/README.md +9 -130
- package/netlify.toml +5 -2
- package/package.json +13 -25
- package/scripts/kb-ingest-docs.ts +203 -0
- package/spine.config.json +65 -4
- package/.framework/migrations/001_schema.sql +0 -425
- package/.framework/migrations/002_rls_and_functions.sql +0 -763
- package/.framework/migrations/004_enhancements.sql +0 -43
- package/.framework/migrations/005_auth_user_trigger.sql +0 -105
- package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
- package/.framework/migrations/016_invites.sql +0 -27
- package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
- package/.framework/migrations/018_apps_required_roles.sql +0 -17
- package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
- package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
- package/.framework/migrations/021_app_domains.sql +0 -112
- package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
- package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
- package/.framework/migrations/024_messages_account_id.sql +0 -9
- package/.framework/migrations/025_retype_threads_messages.sql +0 -240
- package/.framework/migrations/026_fix_rls_policies.sql +0 -47
- package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
- package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
- package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
- package/.framework/migrations/030_messages_updated_by.sql +0 -6
- package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
- package/docs/APPS.md +0 -232
- package/docs/ARCHITECTURE.md +0 -246
- package/docs/INSTALL.md +0 -155
- package/docs/design-schema-spec.md +0 -1837
- package/docs/dev-guides/01-create-an-app.md +0 -183
- package/docs/dev-guides/02-create-a-function.md +0 -178
- package/docs/dev-guides/03-create-a-component.md +0 -178
- package/docs/dev-guides/04-webhook-handlers.md +0 -129
- package/docs/dev-guides/05-testing.md +0 -168
- package/docs/dev-guides/README.md +0 -40
- package/docs/npm-packages-plan.md +0 -288
- package/docs/spine-core--api-reference.md +0 -822
|
@@ -1,549 +1,125 @@
|
|
|
1
|
+
// CIL[BEGIN chunk_id="agent-runner_module_header_1784488696" type="module_header" identity="module_header" parent="none"]
|
|
2
|
+
// CIL[DOC-MACRO purpose="Agent runner module header with imports and type contracts"]
|
|
3
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Declare module dependencies and the core TypeScript interfaces used by agent-runner"]
|
|
4
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/_shared/db.ts,.framework/src/components/ui/ItemListView.tsx,.framework/functions/apps.ts,.framework/functions/integration-routes.ts,.framework/functions/system.ts,.framework/functions/auth.ts,.framework/cli/commands/agents.ts,.framework/functions/types.ts,.framework/cli/commands/migrate.ts"]
|
|
5
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="imports and TypeScript interfaces" errors="none" side_effects="none"]
|
|
6
|
+
// CIL[DOC-MICRO logic_summary="Imports CoreContext, adminDb, emitAudit, createRecord and defines AgentConfig, InferenceResult, ToolCall, ToolResult" notable_decisions="[]"]
|
|
7
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
8
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
9
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
10
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: none. Direct callees: adminDb(.framework/functions/_shared/db.ts),imports(.framework/src/components/ui/ItemListView.tsx),create(.framework/functions/apps.ts),interfaces(.framework/functions/integration-routes.ts),types(.framework/functions/system.ts),context(.framework/functions/auth.ts). Related files: .framework/functions/_shared/db.ts,.framework/src/components/ui/ItemListView.tsx,.framework/functions/apps.ts,.framework/functions/integration-routes.ts,.framework/functions/system.ts,.framework/functions/auth.ts,.framework/cli/commands/agents.ts,.framework/functions/types.ts,.framework/cli/commands/migrate.ts." status="ready"]
|
|
11
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: none. Total affected: 0." status="ready"]
|
|
12
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
13
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [none] and callees [adminDb(.framework/functions/_shared/db.ts),imports(.framework/src/components/ui/ItemListView.tsx),create(.framework/functions/apps.ts),interfaces(.framework/functions/integration-routes.ts),types(.framework/functions/system.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
14
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
15
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1
16
|
/// <reference types="node" />
|
|
2
|
-
|
|
3
|
-
* @module agent-runner
|
|
4
|
-
* @audience both
|
|
5
|
-
* @layer shared-core
|
|
6
|
-
* @stability stable
|
|
7
|
-
*
|
|
8
|
-
* AI agent inference orchestrator with RAG, tool dispatch, and confidence-based
|
|
9
|
-
* escalation. All agent behavior is defined via JSONB configuration stored in
|
|
10
|
-
* existing schema tables — no dedicated migrations are needed.
|
|
11
|
-
*
|
|
12
|
-
* Configuration resolution chain (highest to lowest priority):
|
|
13
|
-
* 1. `thread.data.agent_id` → `thread.type.design_schema.default_agent_id`
|
|
14
|
-
* 2. `thread.data.prompt_config_id` → `agent.metadata.default_prompt_config_id`
|
|
15
|
-
*
|
|
16
|
-
* Inference loop (per `runAgent` call):
|
|
17
|
-
* 1. `resolveAgentConfig` — resolve agent + prompt_config from thread
|
|
18
|
-
* 2. Save user message to `messages` table
|
|
19
|
-
* 3. `executeAgentInference` — iterative tool-call loop (max 5 iterations):
|
|
20
|
-
* a. `buildContext` — assemble system prompt + RAG + history + tools
|
|
21
|
-
* b. `callInference` — call OpenAI-compatible API (or return mock)
|
|
22
|
-
* c. `dispatchTools` — execute any tool_calls via actions table
|
|
23
|
-
* d. Rebuild context with tool results and repeat
|
|
24
|
-
* 4. Confidence check — if below threshold, `handleEscalation`
|
|
25
|
-
* 5. Save agent response to `messages` table
|
|
26
|
-
* 6. Emit `agent.inference.completed` audit log
|
|
27
|
-
*
|
|
28
|
-
* Environment variables used by `callInference`:
|
|
29
|
-
* - `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `LLM_API_KEY` — LLM auth
|
|
30
|
-
* - `OPENAI_BASE_URL` / `LLM_BASE_URL` — API base URL (default: OpenAI)
|
|
31
|
-
* - `LLM_DEFAULT_MODEL` — model name fallback (default: 'gpt-4o')
|
|
32
|
-
*
|
|
33
|
-
* INVARIANT: if no API key is set, `callInference` returns a mock response
|
|
34
|
-
* instead of throwing — safe for local development without credentials.
|
|
35
|
-
* INVARIANT: `runAgent` throws on critical failures (config missing, inference
|
|
36
|
-
* error) — callers must handle the rejection.
|
|
37
|
-
* INVARIANT: `resolveAgentConfig` returns null (not throws) on missing config;
|
|
38
|
-
* `runAgent` converts this to a throw.
|
|
39
|
-
*
|
|
40
|
-
* @seeAlso pipeline-runner.ts (tool dispatch calls runPipeline for run_pipeline tool)
|
|
41
|
-
* @seeAlso audit.ts (emitAudit for agent.inference.* events)
|
|
42
|
-
* @seeAlso index.ts (runAgent, resolveAgentConfig re-exported)
|
|
43
|
-
*/
|
|
44
|
-
|
|
17
|
+
import { createHash } from 'crypto'
|
|
45
18
|
import { CoreContext } from './middleware'
|
|
46
19
|
import { adminDb } from './db'
|
|
47
|
-
import { emitAudit } from './audit'
|
|
20
|
+
import { emitAudit, emitSpan } from './audit'
|
|
21
|
+
import { emitOutcome } from './outcomes'
|
|
22
|
+
import { withSpan } from './tracing'
|
|
48
23
|
import { create as createRecord } from '../admin-data'
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Structured result from a single LLM inference call.
|
|
71
|
-
*
|
|
72
|
-
* @outputSpec content: string — agent response text
|
|
73
|
-
* @outputSpec confidence: number — 0-1 score; derived from logprobs or 0.70 default
|
|
74
|
-
* @outputSpec tool_calls: ToolCall[] | undefined — tools the model wants to call
|
|
75
|
-
* @outputSpec metadata: { model, usage, finish_reason } | undefined
|
|
76
|
-
*/
|
|
77
|
-
export interface InferenceResult {
|
|
78
|
-
content: string
|
|
79
|
-
confidence: number
|
|
80
|
-
tool_calls?: ToolCall[]
|
|
81
|
-
metadata?: Record<string, any>
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* A single tool invocation requested by the LLM in an `InferenceResult`.
|
|
86
|
-
*
|
|
87
|
-
* @inputSpec tool: string — action.slug to look up in the actions table
|
|
88
|
-
* @inputSpec params: Record<string, any> — parsed from OpenAI function call arguments
|
|
89
|
-
* @inputSpec id: string — opaque tool_call ID from the LLM response
|
|
90
|
-
*/
|
|
91
|
-
export interface ToolCall {
|
|
92
|
-
tool: string
|
|
93
|
-
params: Record<string, any>
|
|
94
|
-
id: string
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Result of executing a single `ToolCall`.
|
|
99
|
-
*
|
|
100
|
-
* @outputSpec tool: string — mirrors ToolCall.tool
|
|
101
|
-
* @outputSpec result: any — handler return value on success; null on error
|
|
102
|
-
* @outputSpec error: string | undefined — error message if execution failed
|
|
103
|
-
* @outputSpec id: string — mirrors ToolCall.id for correlation
|
|
104
|
-
*/
|
|
105
|
-
export interface ToolResult {
|
|
106
|
-
tool: string
|
|
107
|
-
result: any
|
|
108
|
-
error?: string
|
|
109
|
-
id: string
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ─── PRIMARY EXPORTS ────────────────────────────────────────────────────────────
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Main entry point: run a full agent inference cycle for a user message.
|
|
116
|
-
*
|
|
117
|
-
* Saves the user message, runs the inference loop (with tool calls), checks
|
|
118
|
-
* confidence, saves the agent response, and emits audit logs.
|
|
119
|
-
*
|
|
120
|
-
* @param threadId - UUID of the thread to run inference on
|
|
121
|
-
* @param userMessage - Raw message text from the user
|
|
122
|
-
* @param ctx - CoreContext with accountId, principal, requestId
|
|
123
|
-
* @returns Promise<any> — the saved agent message record from the `messages` table
|
|
124
|
-
* @throws Error — if agent config cannot be resolved, or LLM inference fails
|
|
125
|
-
* @inputSpec threadId: string — valid UUID in threads table
|
|
126
|
-
* @inputSpec userMessage: string — non-empty message text
|
|
127
|
-
* @inputSpec ctx.accountId: string | null — used to scope DB lookups
|
|
128
|
-
* @outputSpec messages row — the inserted agent reply with content and metadata
|
|
129
|
-
* @sideEffects DB write: inserts 2 messages rows (user + agent)
|
|
130
|
-
* @sideEffects DB write: emitAudit (agent.inference.completed or agent.inference.failed)
|
|
131
|
-
* @sideEffects HTTP call: callInference (LLM API)
|
|
132
|
-
* @sideEffects DB write (conditional): handleEscalation if confidence < threshold
|
|
133
|
-
* @calledBy functions/ai-agents.ts handler for POST ?action=run
|
|
134
|
-
* @calledBy v2-custom/ import callers
|
|
135
|
-
* @calls resolveAgentConfig, saveMessage, executeAgentInference,
|
|
136
|
-
* handleEscalation, emitAudit
|
|
137
|
-
* @testUnit tests/unit/agent-runner.test.ts
|
|
138
|
-
* @testIntegration tests/integration/agent-runner.test.ts
|
|
139
|
-
*
|
|
140
|
-
* @example API handler usage
|
|
141
|
-
* ```ts
|
|
142
|
-
* import { runAgent } from './_shared/index'
|
|
143
|
-
* const agentMsg = await runAgent(body.thread_id, body.message, ctx)
|
|
144
|
-
* return agentMsg
|
|
145
|
-
* ```
|
|
146
|
-
*/
|
|
147
|
-
export async function runAgent(
|
|
148
|
-
threadId: string,
|
|
24
|
+
import { callOpenAIEmbeddings, callOpenAIChat } from './llm'
|
|
25
|
+
import { execute as executeOutbound } from '../integration-outbound'
|
|
26
|
+
// CIL[END chunk_id="agent-runner_module_header_1784488696"]
|
|
27
|
+
// CIL[BEGIN chunk_id="agent-runner_buildcontext_1784488696" type="function" identity="buildContext" parent="none"]
|
|
28
|
+
// CIL[DOC-MACRO purpose="Assemble the full prompt context for an inference call"]
|
|
29
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Build context string from system prompt, template, RAG, history, tool results, tools list, and user message"]
|
|
30
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/src/types/auth.ts,.framework/functions/auth.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/item-progress.ts,.framework/src/components/runtime/DataDetailPage.tsx,custom/apps/cortex/pages/community/CommunityPage.tsx,.framework/cli/context.ts"]
|
|
31
|
+
// CIL[DOC-CONTRACT inputs="config: AgentConfig\nuserMessage: string\ntoolResults: ToolResult[]\nctx: CoreContext" outputs="{ context, retrievedSources, meanSimilarity }" errors="catches retrieveKnowledge errors" side_effects="DB reads for knowledge and history"]
|
|
32
|
+
// CIL[DOC-MICRO logic_summary="Concatenates system prompt, optional context template, retrieved knowledge, conversation history, tool results, available tools, and current message" notable_decisions="[]"]
|
|
33
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
34
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
35
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeAgentInference,runAgent. Direct callees: User(.framework/src/types/auth.ts),context(.framework/functions/auth.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),retrieveKnowledge,list(.framework/functions/item-progress.ts),getConversationHistory,title(.framework/src/components/runtime/DataDetailPage.tsx),Message(custom/apps/cortex/pages/community/CommunityPage.tsx). Related files: .framework/src/types/auth.ts,.framework/functions/auth.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/item-progress.ts,.framework/src/components/runtime/DataDetailPage.tsx,custom/apps/cortex/pages/community/CommunityPage.tsx,.framework/cli/context.ts." status="ready"]
|
|
36
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeAgentInference,runAgent. Total affected: 2." status="ready"]
|
|
37
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
38
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeAgentInference,runAgent] and callees [User(.framework/src/types/auth.ts),context(.framework/functions/auth.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),retrieveKnowledge,list(.framework/functions/item-progress.ts),getConversationHistory,title(.framework/src/components/runtime/DataDetailPage.tsx),Message(custom/apps/cortex/pages/community/CommunityPage.tsx)] shape downstream behavior." status="ready"]
|
|
39
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
40
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
41
|
+
async function buildContext(
|
|
42
|
+
config: AgentConfig,
|
|
149
43
|
userMessage: string,
|
|
44
|
+
toolResults: ToolResult[],
|
|
150
45
|
ctx: CoreContext
|
|
151
|
-
): Promise<any> {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (!config) {
|
|
158
|
-
throw new Error(`Could not resolve agent configuration for thread ${threadId}`)
|
|
46
|
+
): Promise<{ context: string; retrievedSources: any[]; meanSimilarity: number }> {
|
|
47
|
+
return withSpan(ctx, 'agent.build_context', async (ctx) => {
|
|
48
|
+
const { agent, promptConfig, thread } = config
|
|
49
|
+
let context = `${agent.system_prompt || 'You are a helpful assistant.'}\n\n`
|
|
50
|
+
if (promptConfig.context_template) {
|
|
51
|
+
context += `${promptConfig.context_template}\n\n`
|
|
159
52
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
// 3. Execute agent with full context
|
|
171
|
-
const agentResponse = await executeAgentInference(
|
|
172
|
-
config,
|
|
173
|
-
userMessage,
|
|
174
|
-
ctx
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
// 4. Check confidence and escalate if needed
|
|
178
|
-
const isEscalated = agentResponse.confidence < (promptConfig.confidence_threshold || 0)
|
|
179
|
-
if (isEscalated) {
|
|
180
|
-
await handleEscalation(
|
|
181
|
-
threadId,
|
|
182
|
-
agentResponse.confidence,
|
|
183
|
-
promptConfig,
|
|
53
|
+
let retrievedSources: any[] = []
|
|
54
|
+
let meanSimilarity = 0
|
|
55
|
+
const retrievalPolicy = promptConfig.metadata?.retrieval_policy || {}
|
|
56
|
+
try {
|
|
57
|
+
const { docs, meanSimilarity: ms } = await retrieveKnowledge(
|
|
58
|
+
userMessage,
|
|
59
|
+
promptConfig.knowledge_sources || [],
|
|
60
|
+
ctx.accountId!,
|
|
61
|
+
retrievalPolicy,
|
|
184
62
|
ctx
|
|
185
63
|
)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
{
|
|
195
|
-
confidence: agentResponse.confidence,
|
|
196
|
-
retrieval_confidence: agentResponse.retrievalConfidence,
|
|
197
|
-
retrieved_sources: (agentResponse.retrievedSources || []).map((s: any) => ({
|
|
198
|
-
document_id: s.document_id,
|
|
199
|
-
title: s.title,
|
|
200
|
-
similarity: s.similarity,
|
|
201
|
-
item_type: s.item_type,
|
|
202
|
-
vector_type: s.vector_type,
|
|
203
|
-
})),
|
|
204
|
-
tool_calls: agentResponse.tool_calls,
|
|
205
|
-
agent_id: agent.id,
|
|
206
|
-
prompt_config_id: promptConfig.id,
|
|
207
|
-
sender_type: 'agent',
|
|
208
|
-
escalated: isEscalated,
|
|
209
|
-
},
|
|
210
|
-
ctx,
|
|
211
|
-
replyTypeSlug
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
// 6. Emit audit log
|
|
215
|
-
await emitAudit(ctx, 'agent.inference.completed', {
|
|
216
|
-
type: 'agent_message',
|
|
217
|
-
id: agentMsg.id,
|
|
218
|
-
account_id: ctx.accountId ?? undefined
|
|
219
|
-
}, {
|
|
220
|
-
thread_id: threadId,
|
|
221
|
-
agent_id: agent.id,
|
|
222
|
-
confidence: agentResponse.confidence,
|
|
223
|
-
retrieval_confidence: agentResponse.retrievalConfidence,
|
|
224
|
-
sources_count: agentResponse.retrievedSources?.length || 0,
|
|
225
|
-
has_tool_calls: !!agentResponse.tool_calls?.length,
|
|
226
|
-
duration_ms: Date.now() - startTime
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
return agentMsg
|
|
230
|
-
|
|
231
|
-
} catch (error: any) {
|
|
232
|
-
// Log failure
|
|
233
|
-
await emitAudit(ctx, 'agent.inference.failed', {
|
|
234
|
-
type: 'agent_message',
|
|
235
|
-
id: 'failed',
|
|
236
|
-
account_id: ctx.accountId ?? undefined
|
|
237
|
-
}, {
|
|
238
|
-
thread_id: threadId,
|
|
239
|
-
error: error.message
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
throw error
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Resolves the agent, prompt config, thread, and thread type for a given thread.
|
|
248
|
-
*
|
|
249
|
-
* Resolution priority:
|
|
250
|
-
* - `agent_id`: `thread.data.agent_id` → `thread.type.design_schema.default_agent_id`
|
|
251
|
-
* - `prompt_config_id`: `thread.data.prompt_config_id` →
|
|
252
|
-
* `agent.metadata.default_prompt_config_id`
|
|
253
|
-
*
|
|
254
|
-
* Returns `null` (does not throw) when any required record is missing. `runAgent`
|
|
255
|
-
* converts a null return to a thrown Error.
|
|
256
|
-
*
|
|
257
|
-
* @param threadId - UUID of the thread
|
|
258
|
-
* @param ctx - CoreContext (requestId used for error logging)
|
|
259
|
-
* @returns Promise<AgentConfig | null> — null if config cannot be resolved
|
|
260
|
-
* @throws never — errors logged to console, returns null
|
|
261
|
-
* @inputSpec threadId: string — valid UUID in threads table
|
|
262
|
-
* @outputSpec AgentConfig | null
|
|
263
|
-
* @sideEffects DB reads: threads (with type join), ai_agents, prompt_configs
|
|
264
|
-
* @calledBy runAgent
|
|
265
|
-
* @testUnit tests/unit/agent-runner.test.ts — 'resolveAgentConfig'
|
|
266
|
-
*/
|
|
267
|
-
export async function resolveAgentConfig(
|
|
268
|
-
threadId: string,
|
|
269
|
-
ctx: CoreContext
|
|
270
|
-
): Promise<AgentConfig | null> {
|
|
271
|
-
// Load thread with its type
|
|
272
|
-
const { data: thread, error: threadError } = await adminDb
|
|
273
|
-
.from('threads')
|
|
274
|
-
.select('*, type:types(*)')
|
|
275
|
-
.eq('id', threadId)
|
|
276
|
-
.single()
|
|
277
|
-
|
|
278
|
-
if (threadError || !thread) {
|
|
279
|
-
console.error(`Thread not found: ${threadId}`, threadError)
|
|
280
|
-
return null
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const threadType = thread.type
|
|
284
|
-
const threadData = thread.data || {}
|
|
285
|
-
|
|
286
|
-
// Resolve agent_id: thread.data > thread.type.design_schema.default_agent_id
|
|
287
|
-
let agentId = threadData.agent_id
|
|
288
|
-
if (!agentId && threadType?.design_schema?.default_agent_id) {
|
|
289
|
-
agentId = threadType.design_schema.default_agent_id
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (!agentId) {
|
|
293
|
-
console.error(`No agent assigned to thread ${threadId}`)
|
|
294
|
-
return null
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Load agent
|
|
298
|
-
const { data: agent, error: agentError } = await adminDb
|
|
299
|
-
.from('ai_agents')
|
|
300
|
-
.select('*')
|
|
301
|
-
.eq('id', agentId)
|
|
302
|
-
.eq('is_active', true)
|
|
303
|
-
.single()
|
|
304
|
-
|
|
305
|
-
if (agentError || !agent) {
|
|
306
|
-
console.error(`Agent not found: ${agentId}`, agentError)
|
|
307
|
-
return null
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const agentMetadata = agent.metadata || {}
|
|
311
|
-
|
|
312
|
-
// Resolve prompt_config_id: thread.data > agent.metadata.default_prompt_config_id
|
|
313
|
-
let promptConfigId = threadData.prompt_config_id
|
|
314
|
-
if (!promptConfigId && agentMetadata.default_prompt_config_id) {
|
|
315
|
-
promptConfigId = agentMetadata.default_prompt_config_id
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (!promptConfigId) {
|
|
319
|
-
console.error(`No prompt config for thread ${threadId}`)
|
|
320
|
-
return null
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Load prompt config
|
|
324
|
-
const { data: promptConfig, error: configError } = await adminDb
|
|
325
|
-
.from('prompt_configs')
|
|
326
|
-
.select('*')
|
|
327
|
-
.eq('id', promptConfigId)
|
|
328
|
-
.eq('is_active', true)
|
|
329
|
-
.single()
|
|
330
|
-
|
|
331
|
-
if (configError || !promptConfig) {
|
|
332
|
-
console.error(`Prompt config not found: ${promptConfigId}`, configError)
|
|
333
|
-
return null
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return { agent, promptConfig, thread, threadType }
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// ─── INFERENCE LOOP ─────────────────────────────────────────────────────────────
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Runs the iterative inference loop: build context → call LLM → dispatch tools
|
|
343
|
-
* → rebuild context → repeat (up to `maxToolIterations`).
|
|
344
|
-
*
|
|
345
|
-
* Returns early when the LLM response has no tool_calls. Stops on last
|
|
346
|
-
* iteration with a note appended if any tool failed.
|
|
347
|
-
*
|
|
348
|
-
* @param config - Resolved AgentConfig
|
|
349
|
-
* @param userMessage - Original user message text
|
|
350
|
-
* @param ctx - CoreContext
|
|
351
|
-
* @param maxToolIterations - Maximum tool-call loops (default: 5)
|
|
352
|
-
* @returns Promise<InferenceResult> — final LLM response with content and confidence
|
|
353
|
-
* @throws Error('Max tool iterations reached') if loop exhausted without convergence
|
|
354
|
-
* @sideEffects HTTP calls: callInference (per iteration)
|
|
355
|
-
* @sideEffects DB reads: messages (conversation history), embeddings (RAG)
|
|
356
|
-
* @calledBy runAgent
|
|
357
|
-
* @calls buildContext, callInference, dispatchTools
|
|
358
|
-
*/
|
|
359
|
-
async function executeAgentInference(
|
|
360
|
-
config: AgentConfig,
|
|
361
|
-
userMessage: string,
|
|
362
|
-
ctx: CoreContext,
|
|
363
|
-
maxToolIterations: number = 5
|
|
364
|
-
): Promise<InferenceResult & { retrievedSources: any[]; retrievalConfidence: number }> {
|
|
365
|
-
const { agent, promptConfig } = config
|
|
366
|
-
|
|
367
|
-
// Build initial context — captures retrieved sources for feedback capture
|
|
368
|
-
let { context, retrievedSources, meanSimilarity } = await buildContext(config, userMessage, [], ctx)
|
|
369
|
-
|
|
370
|
-
for (let iteration = 0; iteration < maxToolIterations; iteration++) {
|
|
371
|
-
const inferenceResult = await callInference(context, agent, promptConfig, ctx)
|
|
372
|
-
|
|
373
|
-
if (!inferenceResult.tool_calls || inferenceResult.tool_calls.length === 0) {
|
|
374
|
-
// Composite confidence: 60% LLM logprobs + 40% retrieval similarity
|
|
375
|
-
// When logprobs are absent, be conservative: default 0.70 instead of 0.85
|
|
376
|
-
// If mean retrieval similarity < 0.40, cap composite at 0.60
|
|
377
|
-
// If mean retrieval similarity < 0.25, cap composite at 0.45 (very poor retrieval)
|
|
378
|
-
const llmConf = inferenceResult.confidence ?? 0.70
|
|
379
|
-
let compositeConf: number
|
|
380
|
-
if (meanSimilarity < 0.25 && meanSimilarity > 0) {
|
|
381
|
-
compositeConf = Math.min(llmConf * 0.6 + meanSimilarity * 0.4, 0.45)
|
|
382
|
-
} else if (meanSimilarity < 0.40 && meanSimilarity > 0) {
|
|
383
|
-
compositeConf = Math.min(llmConf * 0.6 + meanSimilarity * 0.4, 0.60)
|
|
384
|
-
} else {
|
|
385
|
-
compositeConf = llmConf * 0.6 + Math.min(meanSimilarity, 1) * 0.4
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
return {
|
|
389
|
-
...inferenceResult,
|
|
390
|
-
confidence: Math.min(Math.max(compositeConf, 0), 1),
|
|
391
|
-
retrievedSources,
|
|
392
|
-
retrievalConfidence: meanSimilarity,
|
|
64
|
+
retrievedSources = docs
|
|
65
|
+
meanSimilarity = ms
|
|
66
|
+
if (docs.length > 0) {
|
|
67
|
+
context += '## Relevant Information\n'
|
|
68
|
+
docs.forEach((doc, i) => {
|
|
69
|
+
const sourceLabel = doc.title ? `[${i + 1}] ${doc.title}` : `[${i + 1}]`
|
|
70
|
+
context += `${sourceLabel}\n${doc.content}\n\n`
|
|
71
|
+
})
|
|
393
72
|
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.warn('retrieveKnowledge failed — proceeding without context:', err)
|
|
394
75
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
76
|
+
const history = await getConversationHistory(thread.id, promptConfig.max_history_messages || 20)
|
|
77
|
+
if (history.length > 0) {
|
|
78
|
+
context += '## Conversation History\n'
|
|
79
|
+
history.forEach(msg => {
|
|
80
|
+
const role = msg.data?.message_type === 'human' ? 'User' : 'Assistant'
|
|
81
|
+
context += `${role}: ${msg.content}\n`
|
|
82
|
+
})
|
|
83
|
+
context += '\n'
|
|
402
84
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
85
|
+
if (toolResults.length > 0) {
|
|
86
|
+
context += '## Tool Results\n'
|
|
87
|
+
toolResults.forEach(result => {
|
|
88
|
+
if (result.error) {
|
|
89
|
+
context += `Tool "${result.tool}" failed: ${result.error}\n`
|
|
90
|
+
} else {
|
|
91
|
+
context += `Tool "${result.tool}" result: ${JSON.stringify(result.result)}\n`
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
context += '\n'
|
|
408
95
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* Assembles the full prompt context string for a single inference call.
|
|
416
|
-
*
|
|
417
|
-
* Context sections (in order):
|
|
418
|
-
* 1. `agent.system_prompt` (or default 'You are a helpful assistant.')
|
|
419
|
-
* 2. `promptConfig.context_template` (if set)
|
|
420
|
-
* 3. Retrieved knowledge via `retrieveKnowledge` (RAG, if knowledge_sources set)
|
|
421
|
-
* 4. Conversation history via `getConversationHistory`
|
|
422
|
-
* 5. Tool results from previous iteration (if any)
|
|
423
|
-
* 6. Available tools list from `promptConfig.available_tools`
|
|
424
|
-
* 7. Current user message
|
|
425
|
-
*
|
|
426
|
-
* @param config - AgentConfig with agent, promptConfig, thread
|
|
427
|
-
* @param userMessage - Current user message
|
|
428
|
-
* @param toolResults - Results from previous tool dispatch iteration
|
|
429
|
-
* @param ctx - CoreContext
|
|
430
|
-
* @returns Promise<string> — assembled prompt context string
|
|
431
|
-
* @throws never — returns partial context on sub-call failure
|
|
432
|
-
* @sideEffects DB reads: embeddings (retrieveKnowledge), messages (history)
|
|
433
|
-
* @calledBy executeAgentInference (per iteration)
|
|
434
|
-
* @calls retrieveKnowledge, getConversationHistory
|
|
435
|
-
*/
|
|
436
|
-
async function buildContext(
|
|
437
|
-
config: AgentConfig,
|
|
438
|
-
userMessage: string,
|
|
439
|
-
toolResults: ToolResult[],
|
|
440
|
-
ctx: CoreContext
|
|
441
|
-
): Promise<{ context: string; retrievedSources: any[]; meanSimilarity: number }> {
|
|
442
|
-
const { agent, promptConfig, thread } = config
|
|
443
|
-
|
|
444
|
-
// 1. System prompt (from agent)
|
|
445
|
-
let context = `${agent.system_prompt || 'You are a helpful assistant.'}\n\n`
|
|
446
|
-
|
|
447
|
-
// 2. Context template (from prompt config)
|
|
448
|
-
if (promptConfig.context_template) {
|
|
449
|
-
context += `${promptConfig.context_template}\n\n`
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// 3. Retrieved knowledge (RAG)
|
|
453
|
-
// Always attempt retrieval — even with empty knowledge_sources (full scope search)
|
|
454
|
-
let retrievedSources: any[] = []
|
|
455
|
-
let meanSimilarity = 0
|
|
456
|
-
const retrievalPolicy = promptConfig.metadata?.retrieval_policy || {}
|
|
457
|
-
|
|
458
|
-
try {
|
|
459
|
-
const { docs, meanSimilarity: ms } = await retrieveKnowledge(
|
|
460
|
-
userMessage,
|
|
461
|
-
promptConfig.knowledge_sources || [],
|
|
462
|
-
ctx.accountId!,
|
|
463
|
-
retrievalPolicy
|
|
464
|
-
)
|
|
465
|
-
retrievedSources = docs
|
|
466
|
-
meanSimilarity = ms
|
|
467
|
-
|
|
468
|
-
if (docs.length > 0) {
|
|
469
|
-
context += '## Relevant Information\n'
|
|
470
|
-
docs.forEach((doc, i) => {
|
|
471
|
-
const sourceLabel = doc.title ? `[${i + 1}] ${doc.title}` : `[${i + 1}]`
|
|
472
|
-
context += `${sourceLabel}\n${doc.content}\n\n`
|
|
96
|
+
if (promptConfig.available_tools && promptConfig.available_tools.length > 0) {
|
|
97
|
+
context += '## Available Tools\n'
|
|
98
|
+
promptConfig.available_tools.forEach((tool: string) => {
|
|
99
|
+
context += `- ${tool}\n`
|
|
473
100
|
})
|
|
101
|
+
context += '\n'
|
|
474
102
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// 4. Conversation history
|
|
480
|
-
const history = await getConversationHistory(thread.id, promptConfig.max_history_messages || 20)
|
|
481
|
-
if (history.length > 0) {
|
|
482
|
-
context += '## Conversation History\n'
|
|
483
|
-
history.forEach(msg => {
|
|
484
|
-
const role = msg.data?.message_type === 'human' ? 'User' : 'Assistant'
|
|
485
|
-
context += `${role}: ${msg.content}\n`
|
|
486
|
-
})
|
|
487
|
-
context += '\n'
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// 5. Tool results (if any)
|
|
491
|
-
if (toolResults.length > 0) {
|
|
492
|
-
context += '## Tool Results\n'
|
|
493
|
-
toolResults.forEach(result => {
|
|
494
|
-
if (result.error) {
|
|
495
|
-
context += `Tool "${result.tool}" failed: ${result.error}\n`
|
|
496
|
-
} else {
|
|
497
|
-
context += `Tool "${result.tool}" result: ${JSON.stringify(result.result)}\n`
|
|
498
|
-
}
|
|
499
|
-
})
|
|
500
|
-
context += '\n'
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// 6. Available tools (if configured)
|
|
504
|
-
if (promptConfig.available_tools && promptConfig.available_tools.length > 0) {
|
|
505
|
-
context += '## Available Tools\n'
|
|
506
|
-
promptConfig.available_tools.forEach((tool: string) => {
|
|
507
|
-
context += `- ${tool}\n`
|
|
508
|
-
})
|
|
509
|
-
context += '\n'
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// 7. Current user message
|
|
513
|
-
context += `## Current Message\nUser: ${userMessage}\n\nAssistant: `
|
|
514
|
-
|
|
515
|
-
return { context, retrievedSources, meanSimilarity }
|
|
103
|
+
context += `## Current Message\nUser: ${userMessage}\n\nAssistant: `
|
|
104
|
+
return { context, retrievedSources, meanSimilarity }
|
|
105
|
+
}, { onEnd: emitSpan })
|
|
516
106
|
}
|
|
517
|
-
|
|
518
|
-
//
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
* - Always includes the platform KB account (spine-system), excluding
|
|
534
|
-
* rows where metadata.security_level = 'restricted'.
|
|
535
|
-
* - `knowledgeSources` optionally narrows to specific document UUIDs
|
|
536
|
-
* (prompt_config.knowledge_sources); omit or pass [] for full scope.
|
|
537
|
-
*
|
|
538
|
-
* @param query - User message text to embed and search
|
|
539
|
-
* @param knowledgeSources - Optional document UUIDs to restrict search scope
|
|
540
|
-
* @param accountId - Requesting account for scoped access
|
|
541
|
-
* @returns Promise<any[]> — enriched result rows (id, title, content, similarity)
|
|
542
|
-
* @throws never — falls back to text search or [] on any failure
|
|
543
|
-
* @sideEffects HTTP call to OpenAI embeddings API (if API key present)
|
|
544
|
-
* @sideEffects DB read: match_embeddings RPC + items table
|
|
545
|
-
* @calledBy buildContext (if promptConfig.knowledge_sources is non-empty)
|
|
546
|
-
*/
|
|
107
|
+
// CIL[END chunk_id="agent-runner_buildcontext_1784488696"]
|
|
108
|
+
// CIL[BEGIN chunk_id="agent-runner_retrieveknowledge_1784488696" type="function" identity="retrieveKnowledge" parent="none"]
|
|
109
|
+
// CIL[DOC-MACRO purpose="Retrieve relevant documents from the embeddings table for RAG"]
|
|
110
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Run vector similarity search via OpenAI embeddings and match_embeddings RPC, with security filtering and text fallback"]
|
|
111
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/item-progress.ts,.framework/migrations/001_schema.sql,.framework/functions/_shared/db.ts,.framework/src/pages/admin/TypesPage.tsx,custom/apps/cortex/functions/custom_cil-embeddings.ts,.framework/src/components/runtime/DataDetailPage.tsx,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/auth.ts,.framework/migrations/027_embedding_lifecycle.sql,.framework/functions/embeddings.ts"]
|
|
112
|
+
// CIL[DOC-CONTRACT inputs="query: string\nknowledgeSources: string[]\naccountId: string\nretrievalPolicy?: { item_types?: string[], vector_types?: string[], max_security_level?: string, require_approved?: boolean, project_ids?: string[] }\nctx?: CoreContext" outputs="{ docs, meanSimilarity }" errors="returns empty docs on any failure" side_effects="HTTP call to OpenAI embeddings API; DB RPC and items read"]
|
|
113
|
+
// CIL[DOC-MICRO logic_summary="Generates query embedding, runs multi-vector match_embeddings (larger match_count when project_ids are filtered), filters by security level and optional CIL project_id, deduplicates by document, and falls back to full-text search" notable_decisions="[]"]
|
|
114
|
+
// CIL[DOC-LEARNING topic="agent-runner:retrieval_security_level" content="CIL chunks are stored with security_level 'internal'. A prompt_config retrieval_policy with max_security_level='external' filters them out, causing the agent to see no codebase context. Set max_security_level to 'internal' or higher for CIL-augmented support agents."]
|
|
115
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
116
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
117
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: buildContext,executeAgentInference,runAgent. Direct callees: get(.framework/functions/item-progress.ts),embeddings(.framework/migrations/001_schema.sql),adminDb(.framework/functions/_shared/db.ts),Type(.framework/src/pages/admin/TypesPage.tsx),rank(custom/apps/cortex/functions/custom_cil-embeddings.ts),title(.framework/src/components/runtime/DataDetailPage.tsx),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),context(.framework/functions/auth.ts). Related files: .framework/functions/item-progress.ts,.framework/migrations/001_schema.sql,.framework/functions/_shared/db.ts,.framework/src/pages/admin/TypesPage.tsx,custom/apps/cortex/functions/custom_cil-embeddings.ts,.framework/src/components/runtime/DataDetailPage.tsx,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/auth.ts,.framework/migrations/027_embedding_lifecycle.sql,.framework/functions/embeddings.ts." status="ready"]
|
|
118
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: buildContext,executeAgentInference,runAgent. Total affected: 3." status="ready"]
|
|
119
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
120
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [buildContext,executeAgentInference,runAgent] and callees [get(.framework/functions/item-progress.ts),embeddings(.framework/migrations/001_schema.sql),adminDb(.framework/functions/_shared/db.ts),Type(.framework/src/pages/admin/TypesPage.tsx),rank(custom/apps/cortex/functions/custom_cil-embeddings.ts),title(.framework/src/components/runtime/DataDetailPage.tsx),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
121
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
122
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
547
123
|
async function retrieveKnowledge(
|
|
548
124
|
query: string,
|
|
549
125
|
knowledgeSources: string[],
|
|
@@ -553,47 +129,33 @@ async function retrieveKnowledge(
|
|
|
553
129
|
vector_types?: string[]
|
|
554
130
|
max_security_level?: 'external' | 'internal' | 'restricted'
|
|
555
131
|
require_approved?: boolean
|
|
556
|
-
|
|
132
|
+
project_ids?: string[]
|
|
133
|
+
},
|
|
134
|
+
ctx?: CoreContext
|
|
557
135
|
): Promise<{ docs: any[]; meanSimilarity: number }> {
|
|
136
|
+
return withSpan(ctx || ({ trace: undefined } as CoreContext), 'agent.retrieve_knowledge', async (ctx) => {
|
|
558
137
|
const SIMILARITY_THRESHOLD = 0.15
|
|
559
138
|
const MATCH_COUNT = 8
|
|
560
139
|
const policy = retrievalPolicy || {}
|
|
561
140
|
const vectorTypesToSearch = policy.vector_types?.length ? policy.vector_types : ['semantic']
|
|
562
141
|
const maxSecurityLevel = policy.max_security_level || 'internal'
|
|
563
|
-
|
|
564
|
-
// Security level ordering: external < internal < restricted
|
|
565
142
|
const SECURITY_ORDER: Record<string, number> = { external: 0, internal: 1, restricted: 2 }
|
|
566
143
|
const maxSecurityRank = SECURITY_ORDER[maxSecurityLevel] ?? 1
|
|
567
|
-
|
|
568
|
-
|
|
144
|
+
const projectIds = policy.project_ids || []
|
|
145
|
+
const hasProjectFilter = projectIds.length > 0
|
|
569
146
|
try {
|
|
570
|
-
const apiKey = process.env.OPENAI_API_KEY || process.env.LLM_API_KEY
|
|
571
|
-
const baseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1'
|
|
572
|
-
if (!apiKey) throw new Error('no_api_key')
|
|
573
|
-
|
|
574
|
-
// Resolve platform account — always include spine-system KB
|
|
575
147
|
const { data: platformAccount } = await adminDb
|
|
576
148
|
.from('accounts').select('id').eq('slug', 'spine-system').maybeSingle()
|
|
577
149
|
const accountIds: string[] = [accountId]
|
|
578
150
|
if (platformAccount?.id && platformAccount.id !== accountId) accountIds.push(platformAccount.id)
|
|
579
|
-
|
|
580
|
-
// Generate query embedding
|
|
581
|
-
const embedRes = await fetch(`${baseUrl}/embeddings`, {
|
|
582
|
-
method: 'POST',
|
|
583
|
-
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
|
|
584
|
-
body: JSON.stringify({ model: 'text-embedding-3-small', input: query }),
|
|
585
|
-
})
|
|
586
|
-
if (!embedRes.ok) throw new Error(`embed_api_${embedRes.status}`)
|
|
587
|
-
const embedJson: any = await embedRes.json()
|
|
151
|
+
const embedJson = await callOpenAIEmbeddings(ctx, [query])
|
|
588
152
|
const queryEmbedding: number[] = embedJson.data?.[0]?.embedding || []
|
|
589
153
|
if (!queryEmbedding.length) throw new Error('empty_embedding')
|
|
590
|
-
|
|
591
|
-
// Multi-vector search: run all declared vector types and merge
|
|
592
154
|
const allMatches: any[] = []
|
|
593
155
|
for (const vt of vectorTypesToSearch) {
|
|
594
156
|
const { data: matches, error: rpcError } = await adminDb.rpc('match_embeddings', {
|
|
595
157
|
query_embedding: `[${queryEmbedding.join(',')}]`,
|
|
596
|
-
match_count: MATCH_COUNT * 3,
|
|
158
|
+
match_count: MATCH_COUNT * (hasProjectFilter ? 5 : 3),
|
|
597
159
|
filter_account_ids: accountIds,
|
|
598
160
|
filter_vector_type: vt,
|
|
599
161
|
similarity_threshold: SIMILARITY_THRESHOLD,
|
|
@@ -602,23 +164,26 @@ async function retrieveKnowledge(
|
|
|
602
164
|
if (rpcError) throw new Error(`rpc_${rpcError.message}`)
|
|
603
165
|
allMatches.push(...(matches || []))
|
|
604
166
|
}
|
|
605
|
-
|
|
606
|
-
// Hard security filter
|
|
607
167
|
const filtered = allMatches.filter((r: any) => {
|
|
608
168
|
if (r.similarity < SIMILARITY_THRESHOLD) return false
|
|
609
169
|
const secLevel = r.metadata?.security_level || 'internal'
|
|
610
170
|
const rank = SECURITY_ORDER[secLevel] ?? 1
|
|
611
171
|
if (rank > maxSecurityRank) return false
|
|
612
172
|
if (r.account_id !== accountId && secLevel === 'restricted') return false
|
|
173
|
+
// When project_ids are specified, only keep cil_chunk embeddings whose
|
|
174
|
+
// metadata.project_id is in the allowed list. Non-cil_chunk items pass.
|
|
175
|
+
if (hasProjectFilter) {
|
|
176
|
+
const itemType = r.metadata?.item_type
|
|
177
|
+
if (itemType === 'cil_chunk') {
|
|
178
|
+
const projectId = r.metadata?.project_id
|
|
179
|
+
if (!projectId || !projectIds.includes(projectId)) return false
|
|
180
|
+
}
|
|
181
|
+
}
|
|
613
182
|
return true
|
|
614
183
|
})
|
|
615
|
-
|
|
616
|
-
// Narrow to knowledgeSources if specified
|
|
617
184
|
const scoped = knowledgeSources.length > 0
|
|
618
185
|
? filtered.filter((r: any) => knowledgeSources.includes(r.document_id))
|
|
619
186
|
: filtered
|
|
620
|
-
|
|
621
|
-
// Deduplicate — best chunk per document across all vector types
|
|
622
187
|
const bestByDoc = new Map<string, any>()
|
|
623
188
|
for (const r of scoped) {
|
|
624
189
|
const existing = bestByDoc.get(r.document_id)
|
|
@@ -627,14 +192,11 @@ async function retrieveKnowledge(
|
|
|
627
192
|
const deduped = [...bestByDoc.values()]
|
|
628
193
|
.sort((a, b) => b.similarity - a.similarity)
|
|
629
194
|
.slice(0, MATCH_COUNT)
|
|
630
|
-
|
|
631
195
|
if (!deduped.length) return { docs: [], meanSimilarity: 0 }
|
|
632
|
-
|
|
633
|
-
// If top result < 0.50 and summary not already included, try summary fallback
|
|
634
196
|
if (deduped[0]?.similarity < 0.50 && !vectorTypesToSearch.includes('summary')) {
|
|
635
197
|
const { data: summaryMatches } = await adminDb.rpc('match_embeddings', {
|
|
636
198
|
query_embedding: `[${queryEmbedding.join(',')}]`,
|
|
637
|
-
match_count: MATCH_COUNT,
|
|
199
|
+
match_count: MATCH_COUNT * (hasProjectFilter ? 5 : 1),
|
|
638
200
|
filter_account_ids: accountIds,
|
|
639
201
|
filter_vector_type: 'summary',
|
|
640
202
|
similarity_threshold: SIMILARITY_THRESHOLD,
|
|
@@ -649,12 +211,9 @@ async function retrieveKnowledge(
|
|
|
649
211
|
const finalDocs = [...bestByDoc.values()]
|
|
650
212
|
.sort((a, b) => b.similarity - a.similarity)
|
|
651
213
|
.slice(0, MATCH_COUNT)
|
|
652
|
-
|
|
653
|
-
// Enrich with item titles
|
|
654
214
|
const { data: items } = await adminDb
|
|
655
215
|
.from('items').select('id, title').in('id', finalDocs.map((r: any) => r.document_id))
|
|
656
216
|
const itemMap = new Map((items || []).map((i: any) => [i.id, i]))
|
|
657
|
-
|
|
658
217
|
const docs = finalDocs.map((r: any) => ({
|
|
659
218
|
document_id: r.document_id,
|
|
660
219
|
content: r.content,
|
|
@@ -664,46 +223,51 @@ async function retrieveKnowledge(
|
|
|
664
223
|
item_type: r.metadata?.item_type || '',
|
|
665
224
|
vector_type: r.metadata?.vector_type || '',
|
|
666
225
|
}))
|
|
667
|
-
|
|
668
226
|
const meanSimilarity = docs.length
|
|
669
227
|
? docs.reduce((sum: number, d: any) => sum + d.similarity, 0) / docs.length
|
|
670
228
|
: 0
|
|
671
|
-
|
|
672
229
|
return { docs, meanSimilarity }
|
|
673
|
-
|
|
674
230
|
} catch (vectorErr: any) {
|
|
675
|
-
// ── Tier 2: full-text fallback ──────────────────────────────────────
|
|
676
231
|
if (vectorErr.message !== 'no_api_key') {
|
|
677
232
|
console.warn('retrieveKnowledge vector search failed, falling back to text search:', vectorErr.message)
|
|
678
233
|
}
|
|
679
|
-
|
|
234
|
+
let baseQuery = adminDb
|
|
680
235
|
.from('embeddings')
|
|
681
236
|
.select('document_id, content, metadata, account_id')
|
|
682
237
|
.eq('account_id', accountId)
|
|
683
238
|
.eq('metadata->>vector_type', 'semantic')
|
|
684
|
-
.
|
|
239
|
+
.is('revoked_at', null)
|
|
685
240
|
.textSearch('content', query, { type: 'websearch', config: 'english' })
|
|
686
241
|
.limit(5)
|
|
242
|
+
if (policy.item_types?.length) {
|
|
243
|
+
baseQuery = baseQuery.in('metadata->>item_type', policy.item_types)
|
|
244
|
+
}
|
|
245
|
+
if (hasProjectFilter) {
|
|
246
|
+
const ids = projectIds.map((id: string) => `${id}`).join(',')
|
|
247
|
+
baseQuery = baseQuery.or(`metadata->>item_type.neq.cil_chunk,metadata->>project_id.in.(${ids})`)
|
|
248
|
+
}
|
|
687
249
|
const q = knowledgeSources.length > 0 ? baseQuery.in('document_id', knowledgeSources) : baseQuery
|
|
688
250
|
const { data: docs, error } = await q
|
|
689
251
|
if (error) { console.error('Knowledge retrieval fallback failed:', error); return { docs: [], meanSimilarity: 0 } }
|
|
690
252
|
return { docs: docs || [], meanSimilarity: 0 }
|
|
691
253
|
}
|
|
254
|
+
}, { onEnd: emitSpan })
|
|
692
255
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
256
|
+
// CIL[END chunk_id="agent-runner_retrieveknowledge_1784488696"]
|
|
257
|
+
// CIL[BEGIN chunk_id="agent-runner_getconversationhistory_1784488696" type="function" identity="getConversationHistory" parent="none"]
|
|
258
|
+
// CIL[DOC-MACRO purpose="Load recent messages for a thread in chronological order"]
|
|
259
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Fetch messages from the messages table and return oldest-first"]
|
|
260
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/_shared/db.ts,.framework/functions/auth.ts,.framework/migrations/001_schema.sql,.framework/src/apps/admin/index.tsx,.framework/src/components/ui/chart.tsx"]
|
|
261
|
+
// CIL[DOC-CONTRACT inputs="threadId: string\nlimit: number" outputs="messages[] ordered oldest-first" errors="returns [] on failure" side_effects="DB read messages table"]
|
|
262
|
+
// CIL[DOC-MICRO logic_summary="Queries messages by thread_id, orders descending, reverses to ascending, and returns result" notable_decisions="[]"]
|
|
263
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
264
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
265
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: buildContext,executeAgentInference,runAgent. Direct callees: adminDb(.framework/functions/_shared/db.ts),context(.framework/functions/auth.ts). Related files: .framework/functions/_shared/db.ts,.framework/functions/auth.ts,.framework/migrations/001_schema.sql,.framework/src/apps/admin/index.tsx,.framework/src/components/ui/chart.tsx." status="ready"]
|
|
266
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: buildContext,executeAgentInference,runAgent. Total affected: 3." status="ready"]
|
|
267
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
268
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [buildContext,executeAgentInference,runAgent] and callees [adminDb(.framework/functions/_shared/db.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
269
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
270
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
707
271
|
async function getConversationHistory(
|
|
708
272
|
threadId: string,
|
|
709
273
|
limit: number
|
|
@@ -714,101 +278,67 @@ async function getConversationHistory(
|
|
|
714
278
|
.eq('thread_id', threadId)
|
|
715
279
|
.order('created_at', { ascending: false })
|
|
716
280
|
.limit(limit)
|
|
717
|
-
|
|
718
281
|
if (error) {
|
|
719
282
|
console.error('Failed to load history:', error)
|
|
720
283
|
return []
|
|
721
284
|
}
|
|
722
|
-
|
|
723
285
|
return messages?.reverse() || []
|
|
724
286
|
}
|
|
725
|
-
|
|
726
|
-
//
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
* Confidence is derived from `logprobs` if available, otherwise defaults to 0.85.
|
|
741
|
-
*
|
|
742
|
-
* @param context - Assembled context string from buildContext
|
|
743
|
-
* @param agent - Agent record with model_config (model, temperature, max_tokens, tools)
|
|
744
|
-
* @param promptConfig - Prompt config record (unused here beyond model overrides)
|
|
745
|
-
* @param ctx - CoreContext (requestId for logging)
|
|
746
|
-
* @returns Promise<InferenceResult> — content, confidence, tool_calls, metadata
|
|
747
|
-
* @throws Error('Inference failed: <status>') on non-2xx HTTP response
|
|
748
|
-
* @sideEffects HTTP call to `${OPENAI_BASE_URL}/chat/completions`
|
|
749
|
-
* @calledBy executeAgentInference (per iteration)
|
|
750
|
-
*/
|
|
287
|
+
// CIL[END chunk_id="agent-runner_getconversationhistory_1784488696"]
|
|
288
|
+
// CIL[BEGIN chunk_id="agent-runner_callinference_1784488696" type="function" identity="callInference" parent="none"]
|
|
289
|
+
// CIL[DOC-MACRO purpose="Call an OpenAI-compatible chat completions endpoint"]
|
|
290
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Send assembled context to the LLM and return content, confidence, tool_calls, and metadata"]
|
|
291
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/src/pages/admin/TypesPage.tsx,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/auth.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,custom/apps/cortex/functions/custom_cil-callmap.ts,.framework/cli/commands/system.ts"]
|
|
292
|
+
// CIL[DOC-CONTRACT inputs="context: string\nagent: any\npromptConfig: any\nctx: CoreContext" outputs="InferenceResult" errors="throws on non-2xx response" side_effects="HTTP POST to chat completions API"]
|
|
293
|
+
// CIL[DOC-MICRO logic_summary="Builds messages array from context, calls completions, extracts tool_calls and confidence from logprobs or default" notable_decisions="[]"]
|
|
294
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
295
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
296
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeAgentInference,runAgent. Direct callees: Type(.framework/src/pages/admin/TypesPage.tsx),params(.framework/src/components/runtime/DataDetailPage.tsx),context(.framework/functions/auth.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts). Related files: .framework/src/pages/admin/TypesPage.tsx,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/auth.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,custom/apps/cortex/functions/custom_cil-callmap.ts,.framework/cli/commands/system.ts." status="ready"]
|
|
297
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeAgentInference,runAgent. Total affected: 2." status="ready"]
|
|
298
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
299
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeAgentInference,runAgent] and callees [Type(.framework/src/pages/admin/TypesPage.tsx),params(.framework/src/components/runtime/DataDetailPage.tsx),context(.framework/functions/auth.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts)] shape downstream behavior." status="ready"]
|
|
300
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
301
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
751
302
|
async function callInference(
|
|
752
303
|
context: string,
|
|
753
304
|
agent: any,
|
|
754
305
|
promptConfig: any,
|
|
755
306
|
ctx: CoreContext
|
|
756
307
|
): Promise<InferenceResult> {
|
|
308
|
+
return withSpan(ctx, 'agent.inference', async (ctx) => {
|
|
757
309
|
const modelConfig = agent.model_config || {}
|
|
758
|
-
|
|
759
|
-
// Get LLM credentials from environment (safer than DB storage)
|
|
760
|
-
const apiKey = process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY || process.env.LLM_API_KEY
|
|
761
|
-
const baseUrl = process.env.OPENAI_BASE_URL || process.env.LLM_BASE_URL || 'https://api.openai.com/v1'
|
|
762
|
-
|
|
763
|
-
if (!apiKey) {
|
|
764
|
-
// Fallback: return mock response for development
|
|
765
|
-
console.warn('No LLM API key found in environment. Set OPENAI_API_KEY or LLM_API_KEY.')
|
|
766
|
-
return {
|
|
767
|
-
content: `[Mock Response] Received ${context.length} chars of context. Set OPENAI_API_KEY in .env for live inference.`,
|
|
768
|
-
confidence: 0.9,
|
|
769
|
-
tool_calls: undefined,
|
|
770
|
-
metadata: { mock: true }
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
|
|
774
310
|
const model = modelConfig.model || process.env.LLM_DEFAULT_MODEL || 'gpt-4o'
|
|
775
311
|
const temperature = modelConfig.temperature ?? 0.7
|
|
776
312
|
const maxTokens = modelConfig.max_tokens ?? 4000
|
|
313
|
+
const messages = [
|
|
314
|
+
{ role: 'system', content: context.split('\n\n')[0] },
|
|
315
|
+
{ role: 'user', content: context.split('\n\n').slice(1).join('\n\n') }
|
|
316
|
+
]
|
|
777
317
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
headers: {
|
|
782
|
-
'Content-Type': 'application/json',
|
|
783
|
-
'Authorization': `Bearer ${apiKey}`
|
|
784
|
-
},
|
|
785
|
-
body: JSON.stringify({
|
|
786
|
-
model,
|
|
787
|
-
messages: [
|
|
788
|
-
{ role: 'system', content: context.split('\n\n')[0] }, // First paragraph as system
|
|
789
|
-
{ role: 'user', content: context.split('\n\n').slice(1).join('\n\n') } // Rest as user
|
|
790
|
-
],
|
|
318
|
+
let result: any
|
|
319
|
+
try {
|
|
320
|
+
result = await callOpenAIChat(ctx, messages, model, {
|
|
791
321
|
temperature,
|
|
792
322
|
max_tokens: maxTokens,
|
|
793
323
|
...(modelConfig.tools?.length ? { tools: modelConfig.tools, tool_choice: 'auto' } : {})
|
|
794
324
|
})
|
|
795
|
-
})
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
325
|
+
} catch (err: any) {
|
|
326
|
+
if (err?.message?.includes('not found') || err?.message?.includes('credential')) {
|
|
327
|
+
console.warn('No OpenAI integration or credential configured, returning mock inference response')
|
|
328
|
+
return {
|
|
329
|
+
content: `[Mock Response] Received ${context.length} chars of context. Set OPENAI_API_KEY in .env or configure the openai integration for live inference.`,
|
|
330
|
+
confidence: 0.9,
|
|
331
|
+
tool_calls: undefined,
|
|
332
|
+
metadata: { mock: true }
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
throw err
|
|
800
336
|
}
|
|
801
337
|
|
|
802
|
-
const result: any = await response.json()
|
|
803
338
|
const message = result.choices?.[0]?.message
|
|
804
|
-
|
|
805
|
-
// Extract confidence from logprobs if available, otherwise estimate
|
|
806
|
-
// Default is conservative (0.70) when logprobs are absent — retrieval quality
|
|
807
|
-
// should drive the final composite confidence, not an arbitrary high default.
|
|
808
339
|
const confidence = result.choices?.[0]?.logprobs?.content?.[0]?.logprob
|
|
809
340
|
? Math.exp(result.choices[0].logprobs.content[0].logprob)
|
|
810
341
|
: 0.70
|
|
811
|
-
|
|
812
342
|
return {
|
|
813
343
|
content: message?.content || '',
|
|
814
344
|
confidence,
|
|
@@ -823,260 +353,253 @@ async function callInference(
|
|
|
823
353
|
finish_reason: result.choices?.[0]?.finish_reason
|
|
824
354
|
}
|
|
825
355
|
}
|
|
356
|
+
}, { onEnd: emitSpan })
|
|
826
357
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
358
|
+
// CIL[END chunk_id="agent-runner_callinference_1784488696"]
|
|
359
|
+
// CIL[BEGIN chunk_id="agent-runner_executeinferencehandler_1784488696" type="function" identity="executeInferenceHandler" parent="none"]
|
|
360
|
+
// CIL[DOC-MACRO purpose="Legacy webhook-based inference handler"]
|
|
361
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Dispatch inference to an arbitrary webhook URL for backward compatibility"]
|
|
362
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/tests.ts,.framework/src/pages/admin/TypesPage.tsx,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/auth.ts,.framework/functions/_shared/webhook-registry.ts,.framework/functions/invites.ts,.framework/functions/item-progress.ts"]
|
|
363
|
+
// CIL[DOC-CONTRACT inputs="config: any\nctx: CoreContext" outputs="JSON response" errors="throws on non-2xx" side_effects="HTTP call to config.url"]
|
|
364
|
+
// CIL[DOC-MICRO logic_summary="Returns mock if url is unset; otherwise POSTs config JSON to url" notable_decisions="[]"]
|
|
365
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
366
|
+
// CIL[DOC-STABILITY stability="deprecated"]
|
|
367
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: none. Direct callees: handler(.framework/functions/tests.ts),Type(.framework/src/pages/admin/TypesPage.tsx),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),context(.framework/functions/auth.ts). Related files: .framework/functions/tests.ts,.framework/src/pages/admin/TypesPage.tsx,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/auth.ts,.framework/functions/_shared/webhook-registry.ts,.framework/functions/invites.ts,.framework/functions/item-progress.ts." status="ready"]
|
|
368
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: none. Total affected: 0." status="ready"]
|
|
369
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
370
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [none] and callees [handler(.framework/functions/tests.ts),Type(.framework/src/pages/admin/TypesPage.tsx),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
371
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
372
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
837
373
|
async function executeInferenceHandler(
|
|
838
374
|
config: any,
|
|
839
375
|
ctx: CoreContext
|
|
840
376
|
): Promise<any> {
|
|
841
377
|
const { url, method = 'POST', headers = {} } = config
|
|
842
|
-
|
|
843
378
|
if (!url) {
|
|
844
|
-
// Fallback: return mock response for development
|
|
845
379
|
console.warn('No inference URL configured, returning mock response')
|
|
846
380
|
return {
|
|
847
381
|
content: `[Mock] I received your message. In production, this would call ${config.model} via webhook.`,
|
|
848
382
|
confidence: 0.9
|
|
849
383
|
}
|
|
850
384
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
method,
|
|
854
|
-
headers: {
|
|
855
|
-
'Content-Type': 'application/json',
|
|
856
|
-
...headers
|
|
857
|
-
},
|
|
858
|
-
body: JSON.stringify(config)
|
|
385
|
+
const result = await executeOutbound(ctx, {
|
|
386
|
+
integration_slug: 'generic-http',
|
|
387
|
+
action_config: { url, method: method || 'POST', headers, body: config }
|
|
859
388
|
})
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
throw new Error(`Inference failed: ${response.status} ${response.statusText}`)
|
|
389
|
+
if (!result.success) {
|
|
390
|
+
throw new Error(`Inference failed: ${result.response_status} ${result.response_body}`)
|
|
863
391
|
}
|
|
864
|
-
|
|
865
|
-
return await response.json()
|
|
392
|
+
return result.response_body
|
|
866
393
|
}
|
|
867
|
-
|
|
868
|
-
//
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
* @param ctx - CoreContext
|
|
883
|
-
* @returns Promise<ToolResult[]> — one result per input tool call
|
|
884
|
-
* @throws never — per-tool errors captured in result.error
|
|
885
|
-
* @sideEffects DB read: actions table (per tool call)
|
|
886
|
-
* @sideEffects Calls executeToolAction (DB writes, HTTP calls per handler)
|
|
887
|
-
* @calledBy executeAgentInference (after each inference call with tool_calls)
|
|
888
|
-
* @calls executeToolAction
|
|
889
|
-
*/
|
|
394
|
+
// CIL[END chunk_id="agent-runner_executeinferencehandler_1784488696"]
|
|
395
|
+
// CIL[BEGIN chunk_id="agent-runner_dispatchtools_1784488696" type="function" identity="dispatchTools" parent="none"]
|
|
396
|
+
// CIL[DOC-MACRO purpose="Execute a batch of tool calls in sequence"]
|
|
397
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Look up each tool action and dispatch to executeToolAction, collecting ToolResult entries"]
|
|
398
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/tests.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/_shared/db.ts,.framework/functions/auth.ts"]
|
|
399
|
+
// CIL[DOC-CONTRACT inputs="toolCalls: ToolCall[]\nctx: CoreContext" outputs="ToolResult[]" errors="none; per-tool errors captured in result.error" side_effects="DB reads actions table; calls handlers; resolves built-in CIL tools directly"]
|
|
400
|
+
// CIL[DOC-MICRO logic_summary="Iterates toolCalls, routes CIL tool slugs directly to executeToolAction, loads other actions by slug, executes handler, and pushes success or error results" notable_decisions="[]"]
|
|
401
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
402
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
403
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeAgentInference,runAgent. Direct callees: handler(.framework/functions/tests.ts),executeToolAction,params(.framework/src/components/runtime/DataDetailPage.tsx),adminDb(.framework/functions/_shared/db.ts),context(.framework/functions/auth.ts). Related files: .framework/functions/tests.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/_shared/db.ts,.framework/functions/auth.ts." status="ready"]
|
|
404
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeAgentInference,runAgent. Total affected: 2." status="ready"]
|
|
405
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
406
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeAgentInference,runAgent] and callees [handler(.framework/functions/tests.ts),executeToolAction,params(.framework/src/components/runtime/DataDetailPage.tsx),adminDb(.framework/functions/_shared/db.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
407
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
408
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
890
409
|
async function dispatchTools(
|
|
891
410
|
toolCalls: ToolCall[],
|
|
892
411
|
ctx: CoreContext
|
|
893
412
|
): Promise<ToolResult[]> {
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
413
|
+
return withSpan(ctx, 'agent.dispatch_tools', async (ctx) => {
|
|
414
|
+
return Promise.all(toolCalls.map(async (call) => {
|
|
415
|
+
return withSpan(ctx, `agent.tool.${call.tool}`, async (ctx) => {
|
|
416
|
+
try {
|
|
417
|
+
// CIL tools are built into the runner and do not need an actions table row.
|
|
418
|
+
if (CIL_TOOL_SLUGS.has(call.tool)) {
|
|
419
|
+
const result = await executeToolAction({ handler: call.tool }, call.params, ctx)
|
|
420
|
+
return {
|
|
421
|
+
tool: call.tool,
|
|
422
|
+
id: call.id,
|
|
423
|
+
result
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const { data: action, error } = await adminDb
|
|
427
|
+
.from('actions')
|
|
428
|
+
.select('*')
|
|
429
|
+
.eq('slug', call.tool)
|
|
430
|
+
.eq('is_active', true)
|
|
431
|
+
.single()
|
|
432
|
+
if (error || !action) {
|
|
433
|
+
return {
|
|
434
|
+
tool: call.tool,
|
|
435
|
+
id: call.id,
|
|
436
|
+
result: null,
|
|
437
|
+
error: `Tool "${call.tool}" not found`
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const result = await executeToolAction(action, call.params, ctx)
|
|
441
|
+
return {
|
|
442
|
+
tool: call.tool,
|
|
443
|
+
id: call.id,
|
|
444
|
+
result
|
|
445
|
+
}
|
|
446
|
+
} catch (error: any) {
|
|
447
|
+
return {
|
|
448
|
+
tool: call.tool,
|
|
449
|
+
id: call.id,
|
|
450
|
+
result: null,
|
|
451
|
+
error: error.message
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}, { onEnd: emitSpan })
|
|
455
|
+
}))
|
|
456
|
+
}, { onEnd: emitSpan })
|
|
936
457
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
* @returns Promise<any> — handler output
|
|
953
|
-
* @throws Error('Unknown tool handler') on unrecognised action.handler
|
|
954
|
-
* @calledBy dispatchTools
|
|
955
|
-
*/
|
|
458
|
+
// CIL[END chunk_id="agent-runner_dispatchtools_1784488696"]
|
|
459
|
+
// CIL[BEGIN chunk_id="agent-runner_executetoolaction_1784488696" type="function" identity="executeToolAction" parent="none"]
|
|
460
|
+
// CIL[DOC-MACRO purpose="Dispatch a single tool action to its handler"]
|
|
461
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Route action.handler to the correct tool handler implementation"]
|
|
462
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/src/components/ui/ItemListView.tsx,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/tests.ts,.framework/functions/_shared/index.ts,.framework/functions/auth.ts,custom/apps/cortex/functions/custom_cil-callmap.ts"]
|
|
463
|
+
// CIL[DOC-CONTRACT inputs="action: any\nparams: any\nctx: CoreContext" outputs="handler output" errors="throws Unknown tool handler" side_effects="May dynamically import pipeline-runner and call handlers; calls CIL tool handlers"]
|
|
464
|
+
// CIL[DOC-MICRO logic_summary="Dynamically imports pipeline-runner, switches on action.handler, and awaits the matching handler (including built-in CIL tools)" notable_decisions="[]"]
|
|
465
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
466
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
467
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: dispatchTools,executeAgentInference,runAgent. Direct callees: executeSearchKnowledge,executeSendNotification,imports(.framework/src/components/ui/ItemListView.tsx),executeUpdateItem,executeQueryItems,executeCreateRecord,params(.framework/src/components/runtime/DataDetailPage.tsx),handler(.framework/functions/tests.ts),pipeline(.framework/functions/_shared/index.ts),context(.framework/functions/auth.ts). Related files: .framework/src/components/ui/ItemListView.tsx,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/tests.ts,.framework/functions/_shared/index.ts,.framework/functions/auth.ts,custom/apps/cortex/functions/custom_cil-callmap.ts." status="ready"]
|
|
468
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: dispatchTools,executeAgentInference,runAgent. Total affected: 3." status="ready"]
|
|
469
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
470
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [dispatchTools,executeAgentInference,runAgent] and callees [executeSearchKnowledge,executeSendNotification,imports(.framework/src/components/ui/ItemListView.tsx),executeUpdateItem,executeQueryItems,executeCreateRecord,params(.framework/src/components/runtime/DataDetailPage.tsx),handler(.framework/functions/tests.ts),pipeline(.framework/functions/_shared/index.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
471
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
472
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
956
473
|
async function executeToolAction(
|
|
957
474
|
action: any,
|
|
958
475
|
params: any,
|
|
959
476
|
ctx: CoreContext
|
|
960
477
|
): Promise<any> {
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
478
|
+
return withSpan(ctx, `agent.tool_action.${action.handler || 'unknown'}`, async (ctx) => {
|
|
479
|
+
const { runPipeline } = await import('./pipeline-runner')
|
|
480
|
+
switch (action.handler) {
|
|
481
|
+
case 'search_knowledge':
|
|
482
|
+
return await executeSearchKnowledge(params, ctx)
|
|
483
|
+
case 'query_items':
|
|
484
|
+
return await executeQueryItems(params, ctx)
|
|
485
|
+
case 'create_record':
|
|
486
|
+
return await executeCreateRecord(params, ctx)
|
|
487
|
+
case 'update_item':
|
|
488
|
+
return await executeUpdateItem(params, ctx)
|
|
489
|
+
case 'run_pipeline':
|
|
490
|
+
const result = await runPipeline(params.pipeline_id, params.trigger_data || {}, ctx)
|
|
491
|
+
return { success: result.status === 'completed', result }
|
|
492
|
+
case 'send_notification':
|
|
493
|
+
return await executeSendNotification(params, ctx)
|
|
494
|
+
case 'cil_chunk_lookup':
|
|
495
|
+
return await executeCilChunkLookup(params, ctx)
|
|
496
|
+
case 'cil_get_contract':
|
|
497
|
+
return await executeCilGetContract(params, ctx)
|
|
498
|
+
case 'cil_blast_radius':
|
|
499
|
+
return await executeCilBlastRadius(params, ctx)
|
|
500
|
+
case 'cil_hash_validate':
|
|
501
|
+
return await executeCilHashValidate(params, ctx)
|
|
502
|
+
case 'cil_project_status':
|
|
503
|
+
return await executeCilProjectStatus(params, ctx)
|
|
504
|
+
case 'search_logs':
|
|
505
|
+
return await executeSearchLogs(params, ctx)
|
|
506
|
+
case 'search_observability':
|
|
507
|
+
return await executeSearchObservability(params, ctx)
|
|
508
|
+
default:
|
|
509
|
+
throw new Error(`Unknown tool handler: ${action.handler}`)
|
|
510
|
+
}
|
|
511
|
+
}, { onEnd: emitSpan })
|
|
987
512
|
}
|
|
988
|
-
|
|
989
|
-
//
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
513
|
+
// CIL[END chunk_id="agent-runner_executetoolaction_1784488696"]
|
|
514
|
+
// CIL[BEGIN chunk_id="agent-runner_executesearchknowledge_1784488696" type="function" identity="executeSearchKnowledge" parent="none"]
|
|
515
|
+
// CIL[DOC-MACRO purpose="Tool handler: search_knowledge via vector similarity"]
|
|
516
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Run search_similar_embeddings and fall back to text search"]
|
|
517
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/migrations/001_schema.sql,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/_shared/db.ts,.framework/functions/tests.ts,.framework/functions/auth.ts"]
|
|
518
|
+
// CIL[DOC-CONTRACT inputs="params: any\nctx: CoreContext" outputs="{ results, method }" errors="returns text_fallback results on RPC failure" side_effects="DB RPC or text search on embeddings table"]
|
|
519
|
+
// CIL[DOC-MICRO logic_summary="Calls search_similar_embeddings with account scoping; on error runs textSearch on embeddings.content" notable_decisions="[]"]
|
|
520
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
521
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
522
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeToolAction,dispatchTools,executeAgentInference. Direct callees: embeddings(.framework/migrations/001_schema.sql),params(.framework/src/components/runtime/DataDetailPage.tsx),adminDb(.framework/functions/_shared/db.ts),handler(.framework/functions/tests.ts),context(.framework/functions/auth.ts). Related files: .framework/migrations/001_schema.sql,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/_shared/db.ts,.framework/functions/tests.ts,.framework/functions/auth.ts." status="ready"]
|
|
523
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeToolAction,dispatchTools,executeAgentInference. Total affected: 3." status="ready"]
|
|
524
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
525
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeToolAction,dispatchTools,executeAgentInference] and callees [embeddings(.framework/migrations/001_schema.sql),params(.framework/src/components/runtime/DataDetailPage.tsx),adminDb(.framework/functions/_shared/db.ts),handler(.framework/functions/tests.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
526
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
527
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1003
528
|
async function executeSearchKnowledge(
|
|
1004
529
|
params: any,
|
|
1005
530
|
ctx: CoreContext
|
|
1006
531
|
): Promise<any> {
|
|
1007
532
|
const { query, knowledge_sources, limit = 5 } = params
|
|
1008
|
-
|
|
1009
533
|
const { data: results, error } = await adminDb.rpc('search_similar_embeddings', {
|
|
1010
534
|
p_account_id: ctx.accountId,
|
|
1011
535
|
p_model_id: params.model_id || 'text-embedding-ada-002',
|
|
1012
|
-
p_query_embedding: params.embedding,
|
|
536
|
+
p_query_embedding: params.embedding,
|
|
1013
537
|
p_limit: limit,
|
|
1014
538
|
p_threshold: params.threshold || 0.7
|
|
1015
539
|
})
|
|
1016
|
-
|
|
1017
540
|
if (error) {
|
|
1018
|
-
// Fallback to text search
|
|
1019
541
|
const { data: fallback } = await adminDb
|
|
1020
542
|
.from('embeddings')
|
|
1021
543
|
.select('*')
|
|
1022
544
|
.eq('account_id', ctx.accountId)
|
|
1023
545
|
.textSearch('content', query)
|
|
1024
546
|
.limit(limit)
|
|
1025
|
-
|
|
1026
547
|
return { results: fallback || [], method: 'text_fallback' }
|
|
1027
548
|
}
|
|
1028
|
-
|
|
1029
549
|
return { results: results || [], method: 'vector' }
|
|
1030
550
|
}
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
551
|
+
// CIL[END chunk_id="agent-runner_executesearchknowledge_1784488696"]
|
|
552
|
+
// CIL[BEGIN chunk_id="agent-runner_executequeryitems_1784488696" type="function" identity="executeQueryItems" parent="none"]
|
|
553
|
+
// CIL[DOC-MACRO purpose="Tool handler: query_items filtered SELECT"]
|
|
554
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Run an account-scoped, filtered SELECT on a specified table"]
|
|
555
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files="custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/_shared/db.ts,.framework/functions/tests.ts,.framework/functions/auth.ts"]
|
|
556
|
+
// CIL[DOC-CONTRACT inputs="params: any\nctx: CoreContext" outputs="{ items }" errors="throws on DB failure" side_effects="DB read of params.entity table"]
|
|
557
|
+
// CIL[DOC-MICRO logic_summary="Builds adminDb query with account filter and optional key-value filters, then returns rows" notable_decisions="[]"]
|
|
558
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
559
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
560
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeToolAction,dispatchTools,executeAgentInference. Direct callees: failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),params(.framework/src/components/runtime/DataDetailPage.tsx),adminDb(.framework/functions/_shared/db.ts),handler(.framework/functions/tests.ts),context(.framework/functions/auth.ts). Related files: custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/_shared/db.ts,.framework/functions/tests.ts,.framework/functions/auth.ts." status="ready"]
|
|
561
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeToolAction,dispatchTools,executeAgentInference. Total affected: 3." status="ready"]
|
|
562
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
563
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeToolAction,dispatchTools,executeAgentInference] and callees [failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),params(.framework/src/components/runtime/DataDetailPage.tsx),adminDb(.framework/functions/_shared/db.ts),handler(.framework/functions/tests.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
564
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
565
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1042
566
|
async function executeQueryItems(
|
|
1043
567
|
params: any,
|
|
1044
568
|
ctx: CoreContext
|
|
1045
569
|
): Promise<any> {
|
|
1046
570
|
const { entity, filters = {}, limit = 10 } = params
|
|
1047
|
-
|
|
1048
571
|
let query = adminDb
|
|
1049
572
|
.from(entity)
|
|
1050
573
|
.select('*')
|
|
1051
574
|
.eq('account_id', ctx.accountId)
|
|
1052
575
|
.limit(limit)
|
|
1053
|
-
|
|
1054
|
-
// Apply filters
|
|
1055
576
|
Object.entries(filters).forEach(([key, value]) => {
|
|
1056
577
|
query = query.eq(key, value)
|
|
1057
578
|
})
|
|
1058
|
-
|
|
1059
579
|
const { data, error } = await query
|
|
1060
|
-
|
|
1061
580
|
if (error) throw new Error(`Query failed: ${error.message}`)
|
|
1062
581
|
return { items: data || [] }
|
|
1063
582
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
583
|
+
// CIL[END chunk_id="agent-runner_executequeryitems_1784488696"]
|
|
584
|
+
// CIL[BEGIN chunk_id="agent-runner_executecreaterecord_1784488696" type="function" identity="executeCreateRecord" parent="none"]
|
|
585
|
+
// CIL[DOC-MACRO purpose="Tool handler: create_record insert"]
|
|
586
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Insert a record into any table, stamping account_id and audit fields"]
|
|
587
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/_shared/db.ts,.framework/functions/tests.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/auth.ts,.framework/functions/admin-data.ts,.framework/functions/types.ts"]
|
|
588
|
+
// CIL[DOC-CONTRACT inputs="params: any\nctx: CoreContext" outputs="{ record }" errors="throws on DB failure" side_effects="DB write to params.entity table"]
|
|
589
|
+
// CIL[DOC-MICRO logic_summary="Inserts data with account_id, created_by and timestamps, then selects single row" notable_decisions="[]"]
|
|
590
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
591
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
592
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeToolAction,dispatchTools,executeAgentInference. Direct callees: params(.framework/src/components/runtime/DataDetailPage.tsx),adminDb(.framework/functions/_shared/db.ts),handler(.framework/functions/tests.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),context(.framework/functions/auth.ts). Related files: .framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/_shared/db.ts,.framework/functions/tests.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/auth.ts,.framework/functions/admin-data.ts,.framework/functions/types.ts." status="ready"]
|
|
593
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeToolAction,dispatchTools,executeAgentInference. Total affected: 3." status="ready"]
|
|
594
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
595
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeToolAction,dispatchTools,executeAgentInference] and callees [params(.framework/src/components/runtime/DataDetailPage.tsx),adminDb(.framework/functions/_shared/db.ts),handler(.framework/functions/tests.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
596
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
597
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1074
598
|
async function executeCreateRecord(
|
|
1075
599
|
params: any,
|
|
1076
600
|
ctx: CoreContext
|
|
1077
601
|
): Promise<any> {
|
|
1078
602
|
const { entity, data } = params
|
|
1079
|
-
|
|
1080
603
|
const { data: result, error } = await adminDb
|
|
1081
604
|
.from(entity)
|
|
1082
605
|
.insert({
|
|
@@ -1088,27 +611,29 @@ async function executeCreateRecord(
|
|
|
1088
611
|
})
|
|
1089
612
|
.select()
|
|
1090
613
|
.single()
|
|
1091
|
-
|
|
1092
614
|
if (error) throw new Error(`Create failed: ${error.message}`)
|
|
1093
615
|
return { record: result }
|
|
1094
616
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
617
|
+
// CIL[END chunk_id="agent-runner_executecreaterecord_1784488696"]
|
|
618
|
+
// CIL[BEGIN chunk_id="agent-runner_executeupdateitem_1784488696" type="function" identity="executeUpdateItem" parent="none"]
|
|
619
|
+
// CIL[DOC-MACRO purpose="Tool handler: update_item by ID"]
|
|
620
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Update a record by ID, stamping updated_at and updated_by"]
|
|
621
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/timers.ts,.framework/functions/_shared/db.ts,.framework/functions/tests.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/auth.ts"]
|
|
622
|
+
// CIL[DOC-CONTRACT inputs="params: any\nctx: CoreContext" outputs="{ record }" errors="throws on DB failure" side_effects="DB write to params.entity table"]
|
|
623
|
+
// CIL[DOC-MICRO logic_summary="Updates by id with merged data, updated_at, updated_by, and returns single row" notable_decisions="[]"]
|
|
624
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
625
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
626
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeToolAction,dispatchTools,executeAgentInference. Direct callees: update(.framework/functions/timers.ts),adminDb(.framework/functions/_shared/db.ts),handler(.framework/functions/tests.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),params(.framework/src/components/runtime/DataDetailPage.tsx),context(.framework/functions/auth.ts). Related files: .framework/functions/timers.ts,.framework/functions/_shared/db.ts,.framework/functions/tests.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/auth.ts." status="ready"]
|
|
627
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeToolAction,dispatchTools,executeAgentInference. Total affected: 3." status="ready"]
|
|
628
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
629
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeToolAction,dispatchTools,executeAgentInference] and callees [update(.framework/functions/timers.ts),adminDb(.framework/functions/_shared/db.ts),handler(.framework/functions/tests.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),params(.framework/src/components/runtime/DataDetailPage.tsx),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
630
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
631
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1106
632
|
async function executeUpdateItem(
|
|
1107
633
|
params: any,
|
|
1108
634
|
ctx: CoreContext
|
|
1109
635
|
): Promise<any> {
|
|
1110
636
|
const { entity, record_id, data } = params
|
|
1111
|
-
|
|
1112
637
|
const { data: result, error } = await adminDb
|
|
1113
638
|
.from(entity)
|
|
1114
639
|
.update({
|
|
@@ -1119,28 +644,29 @@ async function executeUpdateItem(
|
|
|
1119
644
|
.eq('id', record_id)
|
|
1120
645
|
.select()
|
|
1121
646
|
.single()
|
|
1122
|
-
|
|
1123
647
|
if (error) throw new Error(`Update failed: ${error.message}`)
|
|
1124
648
|
return { record: result }
|
|
1125
649
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
650
|
+
// CIL[END chunk_id="agent-runner_executeupdateitem_1784488696"]
|
|
651
|
+
// CIL[BEGIN chunk_id="agent-runner_executesendnotification_1784488696" type="function" identity="executeSendNotification" parent="none"]
|
|
652
|
+
// CIL[DOC-MACRO purpose="Tool handler: send_notification inserts watchers"]
|
|
653
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Create watcher rows for each recipient"]
|
|
654
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/migrations/001_schema.sql,.framework/functions/tests.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/_shared/db.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/auth.ts"]
|
|
655
|
+
// CIL[DOC-CONTRACT inputs="params: any\nctx: CoreContext" outputs="{ notified_count }" errors="throws on DB failure" side_effects="DB write watchers table"]
|
|
656
|
+
// CIL[DOC-MICRO logic_summary="Maps recipients to watcher rows and inserts batch, returning count" notable_decisions="[]"]
|
|
657
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
658
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
659
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeToolAction,dispatchTools,executeAgentInference. Direct callees: watchers(.framework/migrations/001_schema.sql),handler(.framework/functions/tests.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),adminDb(.framework/functions/_shared/db.ts),params(.framework/src/components/runtime/DataDetailPage.tsx),context(.framework/functions/auth.ts). Related files: .framework/migrations/001_schema.sql,.framework/functions/tests.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/_shared/db.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/auth.ts." status="ready"]
|
|
660
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeToolAction,dispatchTools,executeAgentInference. Total affected: 3." status="ready"]
|
|
661
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
662
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeToolAction,dispatchTools,executeAgentInference] and callees [watchers(.framework/migrations/001_schema.sql),handler(.framework/functions/tests.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),adminDb(.framework/functions/_shared/db.ts),params(.framework/src/components/runtime/DataDetailPage.tsx),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
663
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
664
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1138
665
|
async function executeSendNotification(
|
|
1139
666
|
params: any,
|
|
1140
667
|
ctx: CoreContext
|
|
1141
668
|
): Promise<any> {
|
|
1142
669
|
const { message, recipients = [], entity_type, entity_id } = params
|
|
1143
|
-
|
|
1144
670
|
const notifications = recipients.map((recipientId: string) => ({
|
|
1145
671
|
account_id: ctx.accountId,
|
|
1146
672
|
person_id: recipientId,
|
|
@@ -1150,59 +676,48 @@ async function executeSendNotification(
|
|
|
1150
676
|
is_read: false,
|
|
1151
677
|
created_at: new Date().toISOString()
|
|
1152
678
|
}))
|
|
1153
|
-
|
|
1154
679
|
if (notifications.length > 0) {
|
|
1155
680
|
const { error } = await adminDb.from('watchers').insert(notifications)
|
|
1156
681
|
if (error) throw new Error(`Notification failed: ${error.message}`)
|
|
1157
682
|
}
|
|
1158
|
-
|
|
1159
683
|
return { notified_count: notifications.length }
|
|
1160
684
|
}
|
|
1161
|
-
|
|
1162
|
-
//
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
* @param confidence - The confidence score that triggered escalation
|
|
1177
|
-
* @param promptConfig - Prompt config with escalation settings
|
|
1178
|
-
* @param ctx - CoreContext
|
|
1179
|
-
* @returns Promise<void> — always resolves
|
|
1180
|
-
* @throws never — DB errors are silently dropped (best-effort)
|
|
1181
|
-
* @sideEffects DB write: threads.data (jsonb_set for escalation_status)
|
|
1182
|
-
* @sideEffects DB write: emitAudit (agent.inference.low_confidence)
|
|
1183
|
-
* @sideEffects Calls runPipeline (if escalation_action === 'pipeline')
|
|
1184
|
-
* @calledBy runAgent (when agentResponse.confidence < threshold)
|
|
1185
|
-
*/
|
|
685
|
+
// CIL[END chunk_id="agent-runner_executesendnotification_1784488696"]
|
|
686
|
+
// CIL[BEGIN chunk_id="agent-runner_handleescalation_1784488696" type="function" identity="handleEscalation" parent="none"]
|
|
687
|
+
// CIL[DOC-MACRO purpose="Handle low-confidence escalation for a thread"]
|
|
688
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Mark thread as pending escalation, set target item to human_assigned, emit audit, and run escalation pipeline if configured"]
|
|
689
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/_shared/index.ts,.framework/functions/_shared/db.ts,.framework/functions/timers.ts,.framework/functions/auth.ts,.framework/docs/runtime-types.md,.framework/migrations/001_schema.sql"]
|
|
690
|
+
// CIL[DOC-CONTRACT inputs="threadId: string\nconfidence: number\npromptConfig: any\nctx: CoreContext" outputs="void" errors="none; DB and pipeline failures are logged, not thrown" side_effects="DB writes threads/items/audit; may call runPipeline"]
|
|
691
|
+
// CIL[DOC-MICRO logic_summary="Updates thread.data.escalation_status, sets item status to human_assigned, emits low_confidence audit, and conditionally runs escalation_target pipeline (resolving slug to UUID and swallowing pipeline errors)" notable_decisions="[]"]
|
|
692
|
+
// CIL[DOC-LEARNING topic="agent-runner:escalation_pipeline_lookup" content="The pipelines table has a 'slug' column used as the human-readable pipeline identifier. handleEscalation resolves escalation_target (which may be a slug or name) by querying on slug or name and then merges the resolved pipeline_id into thread.data for subsequent runs."]
|
|
693
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
694
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: runAgent. Direct callees: pipeline(.framework/functions/_shared/index.ts),adminDb(.framework/functions/_shared/db.ts),update(.framework/functions/timers.ts),context(.framework/functions/auth.ts). Related files: .framework/functions/_shared/index.ts,.framework/functions/_shared/db.ts,.framework/functions/timers.ts,.framework/functions/auth.ts,.framework/docs/runtime-types.md,.framework/migrations/001_schema.sql." status="ready"]
|
|
695
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: runAgent. Total affected: 1." status="ready"]
|
|
696
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
697
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [runAgent] and callees [pipeline(.framework/functions/_shared/index.ts),adminDb(.framework/functions/_shared/db.ts),update(.framework/functions/timers.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
698
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
699
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1186
700
|
async function handleEscalation(
|
|
1187
701
|
threadId: string,
|
|
1188
702
|
confidence: number,
|
|
1189
703
|
promptConfig: any,
|
|
1190
704
|
ctx: CoreContext
|
|
1191
705
|
): Promise<void> {
|
|
1192
|
-
|
|
706
|
+
return withSpan(ctx, 'agent.escalation', async (ctx) => {
|
|
707
|
+
const { data: currentThread } = await adminDb
|
|
708
|
+
.from('threads')
|
|
709
|
+
.select('data')
|
|
710
|
+
.eq('id', threadId)
|
|
711
|
+
.single()
|
|
1193
712
|
await adminDb
|
|
1194
713
|
.from('threads')
|
|
1195
714
|
.update({
|
|
1196
|
-
data:
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
})
|
|
715
|
+
data: {
|
|
716
|
+
...(currentThread?.data || {}),
|
|
717
|
+
escalation_status: 'pending'
|
|
718
|
+
}
|
|
1201
719
|
})
|
|
1202
720
|
.eq('id', threadId)
|
|
1203
|
-
|
|
1204
|
-
// If the thread targets an item (e.g., a support ticket), mark that item as
|
|
1205
|
-
// human-assigned so it shows up in the human queue and stops AI responses.
|
|
1206
721
|
const { data: thread } = await adminDb
|
|
1207
722
|
.from('threads')
|
|
1208
723
|
.select('target_type, target_id')
|
|
@@ -1228,8 +743,6 @@ async function handleEscalation(
|
|
|
1228
743
|
.eq('id', thread.target_id)
|
|
1229
744
|
}
|
|
1230
745
|
}
|
|
1231
|
-
|
|
1232
|
-
// Fire trigger event
|
|
1233
746
|
await emitAudit(ctx, 'agent.inference.low_confidence', {
|
|
1234
747
|
type: 'thread',
|
|
1235
748
|
id: threadId,
|
|
@@ -1240,84 +753,906 @@ async function handleEscalation(
|
|
|
1240
753
|
escalation_action: promptConfig.escalation_action,
|
|
1241
754
|
escalation_target: promptConfig.escalation_target
|
|
1242
755
|
})
|
|
1243
|
-
|
|
1244
|
-
// If escalation pipeline configured, run it
|
|
1245
756
|
if (promptConfig.escalation_action === 'pipeline' && promptConfig.escalation_target) {
|
|
1246
757
|
const { runPipeline } = await import('./pipeline-runner')
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
758
|
+
let pipelineId = promptConfig.escalation_target
|
|
759
|
+
|
|
760
|
+
// escalation_target is usually stored as a slug/name (e.g. "support_escalation"),
|
|
761
|
+
// but runPipeline expects a UUID. Resolve the slug if needed.
|
|
762
|
+
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
763
|
+
if (!isUuid.test(pipelineId)) {
|
|
764
|
+
const { data: pipeline, error: pipelineLookupError } = await adminDb
|
|
765
|
+
.from('pipelines')
|
|
766
|
+
.select('id')
|
|
767
|
+
.or(`slug.eq.${pipelineId},name.eq.${pipelineId}`)
|
|
768
|
+
.eq('is_active', true)
|
|
769
|
+
.maybeSingle()
|
|
1259
770
|
|
|
1260
|
-
|
|
771
|
+
if (pipelineLookupError) {
|
|
772
|
+
console.warn(`Escalation pipeline lookup failed for '${pipelineId}': ${pipelineLookupError.message}`)
|
|
773
|
+
return
|
|
774
|
+
}
|
|
775
|
+
if (!pipeline) {
|
|
776
|
+
console.warn(`Escalation pipeline not found: ${pipelineId}`)
|
|
777
|
+
return
|
|
778
|
+
}
|
|
779
|
+
pipelineId = pipeline.id
|
|
780
|
+
}
|
|
1261
781
|
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
//
|
|
782
|
+
try {
|
|
783
|
+
await runPipeline(
|
|
784
|
+
pipelineId,
|
|
785
|
+
{
|
|
786
|
+
thread_id: threadId,
|
|
787
|
+
confidence,
|
|
788
|
+
threshold: promptConfig.confidence_threshold,
|
|
789
|
+
reason: 'low_confidence'
|
|
790
|
+
},
|
|
791
|
+
ctx
|
|
792
|
+
)
|
|
793
|
+
} catch (err: any) {
|
|
794
|
+
console.warn(`Escalation pipeline '${promptConfig.escalation_target}' failed: ${err.message}`)
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}, { onEnd: emitSpan })
|
|
798
|
+
}
|
|
799
|
+
// CIL[END chunk_id="agent-runner_handleescalation_1784488696"]
|
|
800
|
+
// CIL[BEGIN chunk_id="agent-runner_getreplytypeslug_1784488696" type="function" identity="getReplyTypeSlug" parent="none"]
|
|
801
|
+
// CIL[DOC-MACRO purpose="Map a thread type slug to its reply message type slug"]
|
|
802
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Resolve support/community reply type slugs for message persistence"]
|
|
803
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/system.ts,.framework/functions/auth.ts,.framework/functions/types.ts,.framework/functions/_shared/resolve-ids.ts"]
|
|
804
|
+
// CIL[DOC-CONTRACT inputs="threadTypeSlug: string" outputs="string" errors="none" side_effects="none"]
|
|
805
|
+
// CIL[DOC-MICRO logic_summary="Returns support_reply or community_reply for matching thread types, otherwise message" notable_decisions="[]"]
|
|
806
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
807
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
808
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: runAgent. Direct callees: types(.framework/functions/system.ts),context(.framework/functions/auth.ts). Related files: .framework/functions/system.ts,.framework/functions/auth.ts,.framework/functions/types.ts,.framework/functions/_shared/resolve-ids.ts." status="ready"]
|
|
809
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: runAgent. Total affected: 1." status="ready"]
|
|
810
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
811
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [runAgent] and callees [types(.framework/functions/system.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
812
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
813
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
814
|
+
// CIL[DOC-VECTOR type="semantic" content="Maps thread type slugs to reply message type slugs. engineering ticket threads (eng_ticket_thread) produce eng_ticket_comment replies, support threads produce support_reply, community threads produce community_reply, and all other thread types fall back to message." status="ready"]
|
|
1281
815
|
function getReplyTypeSlug(threadTypeSlug: string): string {
|
|
1282
816
|
if (threadTypeSlug === 'support_thread') return 'support_reply'
|
|
1283
817
|
if (threadTypeSlug === 'community_thread') return 'community_reply'
|
|
818
|
+
if (threadTypeSlug === 'eng_ticket_thread') return 'eng_ticket_comment'
|
|
1284
819
|
return 'message'
|
|
1285
820
|
}
|
|
1286
|
-
|
|
821
|
+
// CIL[END chunk_id="agent-runner_getreplytypeslug_1784488696"]
|
|
822
|
+
// CIL[BEGIN chunk_id="agent-runner_savemessage_1784488696" type="function" identity="saveMessage" parent="none"]
|
|
823
|
+
// CIL[DOC-MACRO purpose="Persist a message row in the messages table"]
|
|
824
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Insert a human, agent, system, or tool message with sequence, direction, visibility, and metadata"]
|
|
825
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/_shared/db.ts,.framework/functions/auth.ts,.framework/migrations/001_schema.sql,.framework/functions/ai-interactions.ts,.framework/docs/runtime-types.md"]
|
|
826
|
+
// CIL[DOC-CONTRACT inputs="threadId: string\ncontent: string\nmessageType: string\ndata: any\nctx: CoreContext\nmessageTypeSlug: string\nvisibility?: string" outputs="message row" errors="throws if insert fails" side_effects="DB write messages table via createRecord"]
|
|
827
|
+
// CIL[DOC-MICRO logic_summary="Computes next sequence, direction, visibility, and calls createRecord with type_slug and data payload" notable_decisions=["Message visibility is inherited from the parent thread so an internal Solution AI workbench never accidentally produces public customer messages."]
|
|
828
|
+
// CIL[DOC-LEARNING topic="agent-runner:messages_schema" content="The messages table has no 'sender' column. Message provenance is captured via person_id for human messages and encoded in messageType/data for agent/system/tool messages. Inserting a 'sender' column fails with a 42703 error."]
|
|
829
|
+
// CIL[DOC-LEARNING topic="agent-runner:message_visibility" content="message visibility must be derived from thread.visibility, not from messageType. Agent messages in a private support AI thread should be internal; the same code path running on the public customer thread must remain external. Pass thread.visibility into saveMessage explicitly."]
|
|
830
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
831
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: runAgent. Direct callees: adminDb(.framework/functions/_shared/db.ts),context(.framework/functions/auth.ts). Related files: .framework/functions/_shared/db.ts,.framework/functions/auth.ts,.framework/migrations/001_schema.sql,.framework/functions/ai-interactions.ts,.framework/docs/runtime-types.md." status="ready"]
|
|
832
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: runAgent. Total affected: 1." status="ready"]
|
|
833
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
834
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [runAgent] and callees [adminDb(.framework/functions/_shared/db.ts),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
835
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
836
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1287
837
|
async function saveMessage(
|
|
1288
838
|
threadId: string,
|
|
1289
839
|
content: string,
|
|
1290
840
|
messageType: 'human' | 'agent' | 'system' | 'tool_call' | 'tool_result',
|
|
1291
841
|
data: any,
|
|
1292
842
|
ctx: CoreContext,
|
|
1293
|
-
messageTypeSlug: string = 'message'
|
|
843
|
+
messageTypeSlug: string = 'message',
|
|
844
|
+
visibility?: string
|
|
1294
845
|
): Promise<any> {
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
846
|
+
return withSpan(ctx, 'agent.save_message', async (ctx) => {
|
|
847
|
+
const { count } = await adminDb
|
|
848
|
+
.from('messages')
|
|
849
|
+
.select('id', { count: 'exact', head: true })
|
|
850
|
+
.eq('thread_id', threadId)
|
|
851
|
+
const sequence = (count ?? 0) + 1
|
|
852
|
+
const direction = messageType === 'human' ? 'inbound' : 'outbound'
|
|
853
|
+
const resolvedVisibility = visibility || ((messageType === 'human' || messageType === 'agent') ? 'external' : 'internal')
|
|
854
|
+
const message = await createRecord(ctx, {
|
|
855
|
+
entity: 'messages',
|
|
856
|
+
type_slug: messageTypeSlug,
|
|
857
|
+
thread_id: threadId,
|
|
858
|
+
person_id: messageType === 'human' ? ctx.principal?.id : null,
|
|
859
|
+
content,
|
|
860
|
+
direction,
|
|
861
|
+
sequence,
|
|
862
|
+
visibility: resolvedVisibility,
|
|
863
|
+
data: {
|
|
864
|
+
message_type: messageType,
|
|
865
|
+
...data
|
|
866
|
+
},
|
|
867
|
+
})
|
|
868
|
+
if (!message?.id) throw new Error('Failed to save message')
|
|
869
|
+
return message
|
|
870
|
+
}, { onEnd: emitSpan })
|
|
871
|
+
}
|
|
872
|
+
// CIL[END chunk_id="agent-runner_savemessage_1784488696"]
|
|
873
|
+
// CIL[BEGIN chunk_id="agent-runner_exports_1784488696" type="interface" identity="exports" parent="none"]
|
|
874
|
+
// CIL[DOC-MACRO purpose="Exported type contracts for agent runner"]
|
|
875
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Define AgentConfig, InferenceResult, ToolCall, and ToolResult shapes"]
|
|
876
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/integration-routes.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/auth.ts,.framework/docs/runtime-types.md"]
|
|
877
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="TypeScript interfaces" errors="none" side_effects="none"]
|
|
878
|
+
// CIL[DOC-MICRO logic_summary="Declares configuration, result, tool call, and tool result structures used across the module" notable_decisions="[]"]
|
|
879
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
880
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
881
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: none. Direct callees: interfaces(.framework/functions/integration-routes.ts),params(.framework/src/components/runtime/DataDetailPage.tsx),context(.framework/functions/auth.ts). Related files: .framework/functions/integration-routes.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/auth.ts,.framework/docs/runtime-types.md." status="ready"]
|
|
882
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: none. Total affected: 0." status="ready"]
|
|
883
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
884
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [none] and callees [interfaces(.framework/functions/integration-routes.ts),params(.framework/src/components/runtime/DataDetailPage.tsx),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
885
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
886
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
887
|
+
export interface AgentConfig {
|
|
888
|
+
agent: any
|
|
889
|
+
promptConfig: any
|
|
890
|
+
thread: any
|
|
891
|
+
threadType: any
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
export interface InferenceResult {
|
|
895
|
+
content: string
|
|
896
|
+
confidence: number
|
|
897
|
+
tool_calls?: ToolCall[]
|
|
898
|
+
metadata?: Record<string, any>
|
|
899
|
+
}
|
|
1303
900
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
901
|
+
export interface ToolCall {
|
|
902
|
+
tool: string
|
|
903
|
+
params: Record<string, any>
|
|
904
|
+
id: string
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
export interface ToolResult {
|
|
908
|
+
tool: string
|
|
909
|
+
result: any
|
|
910
|
+
error?: string
|
|
911
|
+
id: string
|
|
912
|
+
}
|
|
913
|
+
// CIL[END chunk_id="agent-runner_exports_1784488696"]
|
|
914
|
+
// CIL[BEGIN chunk_id="agent-runner_ciltools_1784488696" type="constant" identity="ciltools" parent="none"]
|
|
915
|
+
// CIL[DOC-MACRO purpose="CIL tool definitions and helper utilities for the agent runner"]
|
|
916
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Define OpenAI-style function tool schemas for CIL retrieval and provide normalise/compute helpers"]
|
|
917
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files="custom/apps/cortex/functions/custom_cil-agent.ts,custom/apps/cortex/functions/custom_cil-callmap.ts"]
|
|
918
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="CIL_TOOLS constant and helper functions" errors="none" side_effects="none"]
|
|
919
|
+
// CIL[DOC-MICRO logic_summary="Defines CIL tool schemas; exposes normaliseCilChunkContent and computeCilChunkHash to validate stored chunk hashes" notable_decisions="[]"]
|
|
920
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
921
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
922
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
923
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: resolveAgentConfig,dispatchTools,executeToolAction. Direct callees: adminDb(.framework/functions/_shared/db.ts). Related files: custom/apps/cortex/functions/custom_cil-agent.ts,custom/apps/cortex/functions/custom_cil-callmap.ts." status="ready"]
|
|
924
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: resolveAgentConfig,dispatchTools,executeToolAction. Total affected: 3." status="ready"]
|
|
925
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: thread.data.cil_config.use_cil_tools. Test paths: none." status="ready"]
|
|
926
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [resolveAgentConfig,dispatchTools,executeToolAction] and callees [adminDb(.framework/functions/_shared/db.ts)] shape downstream behavior." status="ready"]
|
|
927
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
928
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
929
|
+
|
|
930
|
+
const CIL_TOOLS: any[] = [
|
|
931
|
+
{
|
|
932
|
+
name: 'cil_chunk_lookup',
|
|
933
|
+
description: 'Search for a CIL chunk by name, file path, or purpose. Returns chunk_id, chunk_name, file_path, and macro.',
|
|
934
|
+
parameters: {
|
|
935
|
+
type: 'object',
|
|
936
|
+
properties: {
|
|
937
|
+
q: { type: 'string', description: 'Search term' },
|
|
938
|
+
project_id: { type: 'string', description: 'Optional project UUID to scope the search' },
|
|
939
|
+
limit: { type: 'number', description: 'Max results to return (default 5)' },
|
|
940
|
+
},
|
|
941
|
+
required: ['q'],
|
|
1318
942
|
},
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
name: 'cil_get_contract',
|
|
946
|
+
description: 'Get the full contract and metadata for a specific CIL chunk by its UUID.',
|
|
947
|
+
parameters: {
|
|
948
|
+
type: 'object',
|
|
949
|
+
properties: {
|
|
950
|
+
chunk_id: { type: 'string', description: 'UUID of the cil_chunk item' },
|
|
951
|
+
},
|
|
952
|
+
required: ['chunk_id'],
|
|
953
|
+
},
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
name: 'cil_blast_radius',
|
|
957
|
+
description: 'Find all chunks that transitively call or are called by a given chunk (imports, calls, exports, tested_by). Use before refactoring.',
|
|
958
|
+
parameters: {
|
|
959
|
+
type: 'object',
|
|
960
|
+
properties: {
|
|
961
|
+
chunk_id: { type: 'string', description: 'UUID of the chunk to check callers/callees for' },
|
|
962
|
+
max_depth: { type: 'number', description: 'Maximum BFS hop depth (default 3, max 6)' },
|
|
963
|
+
},
|
|
964
|
+
required: ['chunk_id'],
|
|
965
|
+
},
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
name: 'cil_hash_validate',
|
|
969
|
+
description: 'Check whether a CIL chunk\'s stored hash is current or has drifted (code was changed without updating CIL).',
|
|
970
|
+
parameters: {
|
|
971
|
+
type: 'object',
|
|
972
|
+
properties: {
|
|
973
|
+
chunk_id: { type: 'string', description: 'UUID of the chunk to validate' },
|
|
974
|
+
},
|
|
975
|
+
required: ['chunk_id'],
|
|
976
|
+
},
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
name: 'cil_project_status',
|
|
980
|
+
description: 'Get the full CIL pipeline status for a project: file count, chunk count, embedding coverage, and doc coverage.',
|
|
981
|
+
parameters: {
|
|
982
|
+
type: 'object',
|
|
983
|
+
properties: {
|
|
984
|
+
project_id: { type: 'string', description: 'UUID of the cil_project' },
|
|
985
|
+
},
|
|
986
|
+
required: ['project_id'],
|
|
987
|
+
},
|
|
988
|
+
},
|
|
989
|
+
]
|
|
990
|
+
|
|
991
|
+
const CIL_TOOL_SLUGS = new Set(CIL_TOOLS.map(t => t.name))
|
|
992
|
+
|
|
993
|
+
function normaliseCilChunkContent(raw: string): string {
|
|
994
|
+
const lines = raw.replace(/\r\n/g, '\n').split('\n')
|
|
995
|
+
const filtered = lines.filter(line => {
|
|
996
|
+
const trimmed = line.trim()
|
|
997
|
+
return !trimmed.match(/^(?:(?:\/\/|--|#)\s*|<!--\s*)CIL\[(BEGIN|END)/) &&
|
|
998
|
+
!trimmed.match(/^(?:(?:\/\/|--|#)\s*|<!--\s*)CIL\[DOC-/)
|
|
1319
999
|
})
|
|
1000
|
+
return filtered.join('\n').trim()
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function computeCilChunkHash(content: string): string {
|
|
1004
|
+
return createHash('sha256').update(normaliseCilChunkContent(content), 'utf8').digest('hex').slice(0, 16)
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// CIL[END chunk_id="agent-runner_ciltools_1784488696"]
|
|
1008
|
+
// CIL[BEGIN chunk_id="agent-runner_runagent_1784488696" type="function" identity="runAgent" parent="none"]
|
|
1009
|
+
// CIL[DOC-MACRO purpose="Execute an AI agent conversation turn"]
|
|
1010
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Orchestrate resolve, save user message, run inference, escalate if needed, save response, and audit"]
|
|
1011
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/migrations/001_schema.sql,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/_shared/index.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/auth.ts,.framework/functions/ai-agents.ts,.framework/cli/commands/agents.ts"]
|
|
1012
|
+
// CIL[DOC-CONTRACT inputs="threadId: string\nuserMessage: string\nctx: CoreContext" outputs="agent message row" errors="throws on resolve or inference failure" side_effects="DB reads/writes, audit logs, possible escalation pipeline"]
|
|
1013
|
+
// CIL[DOC-MICRO logic_summary="Resolves config, saves user message, executes agent inference, escalates low confidence, saves response with sources, and emits audit" notable_decisions="[]"]
|
|
1014
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
1015
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
1016
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: none. Direct callees: executeAgentInference,getReplyTypeSlug,logs(.framework/migrations/001_schema.sql),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),saveMessage,pipeline(.framework/functions/_shared/index.ts),resolveAgentConfig,handleEscalation,title(.framework/src/components/runtime/DataDetailPage.tsx),context(.framework/functions/auth.ts). Related files: .framework/migrations/001_schema.sql,custom/apps/cortex/functions/custom_kb-chunker-test.ts,.framework/functions/_shared/index.ts,.framework/src/components/runtime/DataDetailPage.tsx,.framework/functions/auth.ts,.framework/functions/ai-agents.ts,.framework/cli/commands/agents.ts." status="ready"]
|
|
1017
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: none. Total affected: 0." status="ready"]
|
|
1018
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
1019
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [none] and callees [executeAgentInference,getReplyTypeSlug,logs(.framework/migrations/001_schema.sql),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts),saveMessage,pipeline(.framework/functions/_shared/index.ts),resolveAgentConfig,handleEscalation,title(.framework/src/components/runtime/DataDetailPage.tsx),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
1020
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
1021
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1022
|
+
export async function runAgent(
|
|
1023
|
+
threadId: string,
|
|
1024
|
+
userMessage: string,
|
|
1025
|
+
ctx: CoreContext
|
|
1026
|
+
): Promise<any> {
|
|
1027
|
+
return withSpan(ctx, 'agent.runAgent', async (ctx) => {
|
|
1028
|
+
const startTime = Date.now()
|
|
1029
|
+
try {
|
|
1030
|
+
const config = await resolveAgentConfig(threadId, ctx)
|
|
1031
|
+
if (!config) {
|
|
1032
|
+
throw new Error(`Could not resolve agent configuration for thread ${threadId}`)
|
|
1033
|
+
}
|
|
1034
|
+
const { agent, promptConfig, thread, threadType } = config
|
|
1035
|
+
const replyTypeSlug = getReplyTypeSlug(threadType?.slug)
|
|
1036
|
+
const threadVisibility = thread?.visibility || 'external'
|
|
1037
|
+
const userMsg = await saveMessage(threadId, userMessage, 'human', null, ctx, replyTypeSlug, threadVisibility)
|
|
1038
|
+
const maxToolIterations = promptConfig.metadata?.max_tool_iterations ?? 5
|
|
1039
|
+
const agentResponse = await executeAgentInference(
|
|
1040
|
+
config,
|
|
1041
|
+
userMessage,
|
|
1042
|
+
ctx,
|
|
1043
|
+
maxToolIterations
|
|
1044
|
+
)
|
|
1045
|
+
const isEscalated = agentResponse.confidence < (promptConfig.confidence_threshold || 0)
|
|
1046
|
+
if (isEscalated) {
|
|
1047
|
+
await handleEscalation(
|
|
1048
|
+
threadId,
|
|
1049
|
+
agentResponse.confidence,
|
|
1050
|
+
promptConfig,
|
|
1051
|
+
ctx
|
|
1052
|
+
)
|
|
1053
|
+
}
|
|
1054
|
+
const agentMsg = await saveMessage(
|
|
1055
|
+
threadId,
|
|
1056
|
+
agentResponse.content,
|
|
1057
|
+
'agent',
|
|
1058
|
+
{
|
|
1059
|
+
confidence: agentResponse.confidence,
|
|
1060
|
+
retrieval_confidence: agentResponse.retrievalConfidence,
|
|
1061
|
+
retrieved_sources: (agentResponse.retrievedSources || []).map((s: any) => ({
|
|
1062
|
+
document_id: s.document_id,
|
|
1063
|
+
title: s.title,
|
|
1064
|
+
similarity: s.similarity,
|
|
1065
|
+
item_type: s.item_type,
|
|
1066
|
+
vector_type: s.vector_type,
|
|
1067
|
+
})),
|
|
1068
|
+
tool_calls: agentResponse.tool_calls,
|
|
1069
|
+
agent_id: agent.id,
|
|
1070
|
+
prompt_config_id: promptConfig.id,
|
|
1071
|
+
sender_type: 'agent',
|
|
1072
|
+
escalated: isEscalated,
|
|
1073
|
+
},
|
|
1074
|
+
ctx,
|
|
1075
|
+
replyTypeSlug,
|
|
1076
|
+
threadVisibility
|
|
1077
|
+
)
|
|
1078
|
+
await emitAudit(ctx, 'agent.inference.completed', {
|
|
1079
|
+
type: 'agent_message',
|
|
1080
|
+
id: agentMsg.id,
|
|
1081
|
+
account_id: ctx.accountId ?? undefined
|
|
1082
|
+
}, {
|
|
1083
|
+
thread_id: threadId,
|
|
1084
|
+
agent_id: agent.id,
|
|
1085
|
+
confidence: agentResponse.confidence,
|
|
1086
|
+
retrieval_confidence: agentResponse.retrievalConfidence,
|
|
1087
|
+
sources_count: agentResponse.retrievedSources?.length || 0,
|
|
1088
|
+
has_tool_calls: !!agentResponse.tool_calls?.length,
|
|
1089
|
+
duration_ms: Date.now() - startTime
|
|
1090
|
+
})
|
|
1091
|
+
await emitOutcome(ctx, 'agent.outcome', 'success', {
|
|
1092
|
+
thread_id: threadId,
|
|
1093
|
+
agent_id: agent.id,
|
|
1094
|
+
confidence: agentResponse.confidence,
|
|
1095
|
+
escalated: isEscalated
|
|
1096
|
+
})
|
|
1097
|
+
return agentMsg
|
|
1098
|
+
} catch (error: any) {
|
|
1099
|
+
await emitAudit(ctx, 'agent.inference.failed', {
|
|
1100
|
+
type: 'agent_message',
|
|
1101
|
+
id: 'failed',
|
|
1102
|
+
account_id: ctx.accountId ?? undefined
|
|
1103
|
+
}, {
|
|
1104
|
+
thread_id: threadId,
|
|
1105
|
+
error: error.message
|
|
1106
|
+
})
|
|
1107
|
+
await emitOutcome(ctx, 'agent.outcome', 'failure', {
|
|
1108
|
+
thread_id: threadId,
|
|
1109
|
+
error: error.message
|
|
1110
|
+
})
|
|
1111
|
+
throw error
|
|
1112
|
+
}
|
|
1113
|
+
}, { onEnd: emitSpan })
|
|
1114
|
+
}
|
|
1115
|
+
// CIL[END chunk_id="agent-runner_runagent_1784488696"]
|
|
1116
|
+
// CIL[BEGIN chunk_id="agent-runner_resolveagentconfig_1784488696" type="function" identity="resolveAgentConfig" parent="none"]
|
|
1117
|
+
// CIL[DOC-LEARNING topic="runtime-type:thread/default_agent" content="agent-runner resolves thread.data.agent_id first, then falls back to thread.type.design_schema.default_agent_id; this lets a thread type define its default AI agent without storing it on every thread."]
|
|
1118
|
+
// CIL[DOC-NOTE type="design_schema_caveat" content="If neither thread.data.agent_id nor the thread type's design_schema.default_agent_id is set, the runner logs an error and returns null."]
|
|
1119
|
+
// CIL[DOC-MACRO purpose="Resolve agent, prompt config, and thread type from a thread ID"]
|
|
1120
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Load thread with type, resolve agent_id and prompt_config_id, then fetch agent and prompt config records"]
|
|
1121
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/migrations/001_schema.sql,.framework/functions/system.ts,.framework/functions/_shared/db.ts,custom/apps/cortex/pages/community/CommunityPage.tsx,.framework/functions/auth.ts,.framework/functions/_shared/resolve-ids.ts,.framework/docs/runtime-types.md"]
|
|
1122
|
+
// CIL[DOC-CONTRACT inputs="threadId: string\nctx: CoreContext" outputs="AgentConfig or null" errors="logs and returns null when thread, agent, or prompt config missing" side_effects="DB reads threads, ai_agents, prompt_configs, types; may mutate promptConfig.metadata and agent.model_config.tools with thread.data.cil_config"]
|
|
1123
|
+
// CIL[DOC-MICRO logic_summary="Fetches thread with type, chooses thread.data.agent_id or design_schema.default_agent_id, loads active agent, resolves prompt config, merges thread.data.cil_config into promptConfig.metadata.retrieval_policy when enabled, optionally injects CIL tool definitions into agent.model_config.tools and promptConfig.available_tools, and returns config object" notable_decisions="[]"]
|
|
1124
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
1125
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
1126
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: runAgent. Direct callees: ai_agents(.framework/migrations/001_schema.sql),types(.framework/functions/system.ts),adminDb(.framework/functions/_shared/db.ts),logs(.framework/migrations/001_schema.sql),Thread(custom/apps/cortex/pages/community/CommunityPage.tsx),context(.framework/functions/auth.ts). Related files: .framework/migrations/001_schema.sql,.framework/functions/system.ts,.framework/functions/_shared/db.ts,custom/apps/cortex/pages/community/CommunityPage.tsx,.framework/functions/auth.ts,.framework/functions/_shared/resolve-ids.ts,.framework/docs/runtime-types.md." status="ready"]
|
|
1127
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: runAgent. Total affected: 1." status="ready"]
|
|
1128
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
1129
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [runAgent] and callees [ai_agents(.framework/migrations/001_schema.sql),types(.framework/functions/system.ts),adminDb(.framework/functions/_shared/db.ts),logs(.framework/migrations/001_schema.sql),Thread(custom/apps/cortex/pages/community/CommunityPage.tsx),context(.framework/functions/auth.ts)] shape downstream behavior." status="ready"]
|
|
1130
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
1131
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1132
|
+
export async function resolveAgentConfig(
|
|
1133
|
+
threadId: string,
|
|
1134
|
+
ctx: CoreContext
|
|
1135
|
+
): Promise<AgentConfig | null> {
|
|
1136
|
+
const { data: thread, error: threadError } = await adminDb
|
|
1137
|
+
.from('threads')
|
|
1138
|
+
.select('*, type:types(*)')
|
|
1139
|
+
.eq('id', threadId)
|
|
1140
|
+
.single()
|
|
1141
|
+
if (threadError || !thread) {
|
|
1142
|
+
console.error(`Thread not found: ${threadId}`, threadError)
|
|
1143
|
+
return null
|
|
1144
|
+
}
|
|
1145
|
+
const threadType = thread.type
|
|
1146
|
+
const threadData = thread.data || {}
|
|
1147
|
+
let agentId = threadData.agent_id
|
|
1148
|
+
if (!agentId && threadType?.design_schema?.default_agent_id) {
|
|
1149
|
+
agentId = threadType.design_schema.default_agent_id
|
|
1150
|
+
}
|
|
1151
|
+
if (!agentId) {
|
|
1152
|
+
console.error(`No agent assigned to thread ${threadId}`)
|
|
1153
|
+
return null
|
|
1154
|
+
}
|
|
1155
|
+
const { data: agent, error: agentError } = await adminDb
|
|
1156
|
+
.from('ai_agents')
|
|
1157
|
+
.select('*')
|
|
1158
|
+
.eq('id', agentId)
|
|
1159
|
+
.eq('is_active', true)
|
|
1160
|
+
.single()
|
|
1161
|
+
if (agentError || !agent) {
|
|
1162
|
+
console.error(`Agent not found: ${agentId}`, agentError)
|
|
1163
|
+
return null
|
|
1164
|
+
}
|
|
1165
|
+
const agentMetadata = agent.metadata || {}
|
|
1166
|
+
let promptConfigId = threadData.prompt_config_id
|
|
1167
|
+
if (!promptConfigId && agentMetadata.default_prompt_config_id) {
|
|
1168
|
+
promptConfigId = agentMetadata.default_prompt_config_id
|
|
1169
|
+
}
|
|
1170
|
+
if (!promptConfigId) {
|
|
1171
|
+
console.error(`No prompt config for thread ${threadId}`)
|
|
1172
|
+
return null
|
|
1173
|
+
}
|
|
1174
|
+
const { data: promptConfig, error: configError } = await adminDb
|
|
1175
|
+
.from('prompt_configs')
|
|
1176
|
+
.select('*')
|
|
1177
|
+
.eq('id', promptConfigId)
|
|
1178
|
+
.eq('is_active', true)
|
|
1179
|
+
.single()
|
|
1180
|
+
if (configError || !promptConfig) {
|
|
1181
|
+
console.error(`Prompt config not found: ${promptConfigId}`, configError)
|
|
1182
|
+
return null
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// Merge optional CIL augmentation config from the thread into the prompt config
|
|
1186
|
+
// retrieval policy. This lets app-level manifest config (e.g. portal) augment
|
|
1187
|
+
// support AI with CIL chunks without changing the ai_agents/prompt_configs rows.
|
|
1188
|
+
if (threadData.cil_config?.enabled) {
|
|
1189
|
+
const existingMetadata = promptConfig.metadata || {}
|
|
1190
|
+
const existingPolicy = existingMetadata.retrieval_policy || {}
|
|
1191
|
+
const existingItemTypes = existingPolicy.item_types
|
|
1192
|
+
// If the prompt config already constrains item_types, add cil_chunk to the
|
|
1193
|
+
// list. If it does not, leave item_types unset so match_embeddings searches
|
|
1194
|
+
// all item types; project_ids will still scope CIL chunks to the configured
|
|
1195
|
+
// projects. This preserves KB/article retrieval and augments with CIL.
|
|
1196
|
+
const newItemTypes = existingItemTypes?.length
|
|
1197
|
+
? Array.from(new Set([...existingItemTypes, 'cil_chunk']))
|
|
1198
|
+
: undefined
|
|
1199
|
+
promptConfig.metadata = {
|
|
1200
|
+
...existingMetadata,
|
|
1201
|
+
retrieval_policy: {
|
|
1202
|
+
...existingPolicy,
|
|
1203
|
+
...(newItemTypes ? { item_types: newItemTypes } : {}),
|
|
1204
|
+
project_ids: threadData.cil_config.project_ids || [],
|
|
1205
|
+
},
|
|
1206
|
+
cil_config: threadData.cil_config,
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// When the portal/app config asks for CIL tools, inject them into the agent
|
|
1210
|
+
// and prompt config so the LLM can dispatch CIL lookups during the run.
|
|
1211
|
+
if (threadData.cil_config.use_cil_tools) {
|
|
1212
|
+
agent.model_config = agent.model_config || {}
|
|
1213
|
+
const existingTools = agent.model_config.tools || []
|
|
1214
|
+
const cilToolDefs = CIL_TOOLS.map((t: any) => ({ type: 'function', function: t }))
|
|
1215
|
+
agent.model_config.tools = [...existingTools, ...cilToolDefs]
|
|
1216
|
+
promptConfig.available_tools = [...(promptConfig.available_tools || []), ...CIL_TOOLS.map((t: any) => t.name)]
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1320
1219
|
|
|
1321
|
-
|
|
1322
|
-
|
|
1220
|
+
return { agent, promptConfig, thread, threadType }
|
|
1221
|
+
}
|
|
1222
|
+
// CIL[END chunk_id="agent-runner_resolveagentconfig_1784488696"]
|
|
1223
|
+
// CIL[BEGIN chunk_id="agent-runner_executeagentinference_1784488696" type="function" identity="executeAgentInference" parent="none"]
|
|
1224
|
+
// CIL[DOC-MACRO purpose="Run agent inference with optional tool-calling loop"]
|
|
1225
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Build context, call LLM, dispatch tool calls, and compute composite confidence from logprobs and retrieval similarity"]
|
|
1226
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/functions/auth.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,custom/apps/portal/hooks/useTickets.ts"]
|
|
1227
|
+
// CIL[DOC-CONTRACT inputs="config: AgentConfig\nuserMessage: string\nctx: CoreContext\nmaxToolIterations?: number" outputs="InferenceResult with retrievedSources and retrievalConfidence" errors="throws on max iterations or sub-call failure" side_effects="Calls buildContext, callInference, dispatchTools"]
|
|
1228
|
+
// CIL[DOC-MICRO logic_summary="Builds context, loops up to max iterations calling inference and dispatching tools, blends logprob and retrieval similarity for final confidence" notable_decisions="[]"]
|
|
1229
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
1230
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
1231
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: runAgent. Direct callees: callInference,dispatchTools,buildContext,context(.framework/functions/auth.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts). Related files: .framework/functions/auth.ts,custom/apps/cortex/functions/custom_kb-chunker-test.ts,custom/apps/portal/hooks/useTickets.ts." status="ready"]
|
|
1232
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: runAgent. Total affected: 1." status="ready"]
|
|
1233
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
1234
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [runAgent] and callees [callInference,dispatchTools,buildContext,context(.framework/functions/auth.ts),failed(custom/apps/cortex/functions/custom_kb-chunker-test.ts)] shape downstream behavior." status="ready"]
|
|
1235
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
1236
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1237
|
+
// CIL[DOC-NOTE type="tool_loop_limit" content="When the last configured tool iteration still produces tool calls, the runner injects a system note asking the LLM to synthesize a final answer instead of throwing an error. This prevents 'Max tool iterations reached' failures while still capping tool usage via max_tool_iterations."]
|
|
1238
|
+
async function executeAgentInference(
|
|
1239
|
+
config: AgentConfig,
|
|
1240
|
+
userMessage: string,
|
|
1241
|
+
ctx: CoreContext,
|
|
1242
|
+
maxToolIterations: number = 5
|
|
1243
|
+
): Promise<InferenceResult & { retrievedSources: any[]; retrievalConfidence: number }> {
|
|
1244
|
+
return withSpan(ctx, 'agent.execute_inference', async (ctx) => {
|
|
1245
|
+
const { agent, promptConfig } = config
|
|
1246
|
+
let { context, retrievedSources, meanSimilarity } = await buildContext(config, userMessage, [], ctx)
|
|
1247
|
+
const computeComposite = (llmConf: number): number => {
|
|
1248
|
+
if (meanSimilarity < 0.25 && meanSimilarity > 0) {
|
|
1249
|
+
return Math.min(llmConf * 0.6 + meanSimilarity * 0.4, 0.45)
|
|
1250
|
+
} else if (meanSimilarity < 0.40 && meanSimilarity > 0) {
|
|
1251
|
+
return Math.min(llmConf * 0.6 + meanSimilarity * 0.4, 0.60)
|
|
1252
|
+
}
|
|
1253
|
+
return llmConf * 0.6 + Math.min(meanSimilarity, 1) * 0.4
|
|
1254
|
+
}
|
|
1255
|
+
for (let iteration = 0; iteration < maxToolIterations; iteration++) {
|
|
1256
|
+
const inferenceResult = await callInference(context, agent, promptConfig, ctx)
|
|
1257
|
+
if (!inferenceResult.tool_calls || inferenceResult.tool_calls.length === 0) {
|
|
1258
|
+
const compositeConf = computeComposite(inferenceResult.confidence ?? 0.70)
|
|
1259
|
+
return {
|
|
1260
|
+
...inferenceResult,
|
|
1261
|
+
confidence: Math.min(Math.max(compositeConf, 0), 1),
|
|
1262
|
+
retrievedSources,
|
|
1263
|
+
retrievalConfidence: meanSimilarity,
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
const toolResults = await dispatchTools(inferenceResult.tool_calls, ctx)
|
|
1267
|
+
const rebuilt = await buildContext(config, userMessage, toolResults, ctx)
|
|
1268
|
+
context = rebuilt.context
|
|
1269
|
+
if (rebuilt.retrievedSources.length) {
|
|
1270
|
+
retrievedSources = rebuilt.retrievedSources
|
|
1271
|
+
meanSimilarity = rebuilt.meanSimilarity
|
|
1272
|
+
}
|
|
1273
|
+
if (iteration === maxToolIterations - 1) {
|
|
1274
|
+
const hasErrors = toolResults.some(r => r.error)
|
|
1275
|
+
const note = `\n\n[System note: You have reached the tool limit. Do not call any more tools. Synthesize your final answer based on the information and tool results already provided.${hasErrors ? ' Some tools failed.' : ''}]`
|
|
1276
|
+
const finalResult = await callInference(context + note, agent, promptConfig, ctx)
|
|
1277
|
+
if (finalResult.tool_calls?.length) {
|
|
1278
|
+
finalResult.content = (finalResult.content || '') + '\n\n[Note: I reached the tool limit and still attempted to call tools. The response may be incomplete.]'
|
|
1279
|
+
}
|
|
1280
|
+
const compositeConf = computeComposite(finalResult.confidence ?? 0.70)
|
|
1281
|
+
return {
|
|
1282
|
+
...finalResult,
|
|
1283
|
+
confidence: Math.min(Math.max(compositeConf, 0), 1),
|
|
1284
|
+
retrievedSources,
|
|
1285
|
+
retrievalConfidence: meanSimilarity,
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
throw new Error('Max tool iterations reached')
|
|
1290
|
+
}, { onEnd: emitSpan })
|
|
1291
|
+
}
|
|
1292
|
+
// CIL[END chunk_id="agent-runner_executeagentinference_1784488696"]
|
|
1293
|
+
// CIL[BEGIN chunk_id="agent-runner_executecilchunklookup_1784488696" type="function" identity="executeCilChunkLookup" parent="agent-runner_ciltools_1784488696"]
|
|
1294
|
+
// CIL[DOC-MACRO purpose="Tool handler: cil_chunk_lookup"]
|
|
1295
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Search CIL chunks by name, file path, or purpose"]
|
|
1296
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files="custom/apps/cortex/functions/custom_cil-agent.ts"]
|
|
1297
|
+
// CIL[DOC-CONTRACT inputs="params: { q, project_id?, limit? }\nctx: CoreContext" outputs="{ results, count }" errors="throws on DB failure" side_effects="DB read on items table"]
|
|
1298
|
+
// CIL[DOC-MICRO logic_summary="Queries active cil_chunk items for the account, filters by project_id and search term, and returns a trimmed result list" notable_decisions="[]"]
|
|
1299
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
1300
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
1301
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
1302
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeToolAction. Direct callees: adminDb(.framework/functions/_shared/db.ts). Related files: custom/apps/cortex/functions/custom_cil-agent.ts." status="ready"]
|
|
1303
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeToolAction. Total affected: 1." status="ready"]
|
|
1304
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
1305
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeToolAction] and callees [adminDb(.framework/functions/_shared/db.ts)] shape downstream behavior." status="ready"]
|
|
1306
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
1307
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1308
|
+
async function executeCilChunkLookup(params: any, ctx: CoreContext): Promise<any> {
|
|
1309
|
+
const { q, project_id, limit = 5 } = params
|
|
1310
|
+
if (!q) throw new Error('q is required')
|
|
1311
|
+
|
|
1312
|
+
let query = adminDb
|
|
1313
|
+
.from('items')
|
|
1314
|
+
.select('id, title, data')
|
|
1315
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
1316
|
+
.eq('account_id', ctx.accountId)
|
|
1317
|
+
.eq('is_active', true)
|
|
1318
|
+
.limit(50)
|
|
1319
|
+
|
|
1320
|
+
if (project_id) {
|
|
1321
|
+
query = query.filter('data->>project_id', 'eq', project_id)
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
const { data, error } = await query
|
|
1325
|
+
if (error) throw error
|
|
1326
|
+
|
|
1327
|
+
const term = String(q).toLowerCase()
|
|
1328
|
+
const matches = (data || [])
|
|
1329
|
+
.filter((item: any) => {
|
|
1330
|
+
const d = item.data || {}
|
|
1331
|
+
const haystack = [
|
|
1332
|
+
item.title || '',
|
|
1333
|
+
d.chunk_name || '',
|
|
1334
|
+
d.file_path || '',
|
|
1335
|
+
d.macro || '',
|
|
1336
|
+
d.micro || '',
|
|
1337
|
+
].join(' ').toLowerCase()
|
|
1338
|
+
return haystack.includes(term)
|
|
1339
|
+
})
|
|
1340
|
+
.slice(0, limit)
|
|
1341
|
+
.map((item: any) => {
|
|
1342
|
+
const d = item.data || {}
|
|
1343
|
+
return {
|
|
1344
|
+
chunk_id: item.id,
|
|
1345
|
+
chunk_name: d.chunk_name,
|
|
1346
|
+
file_path: d.file_path,
|
|
1347
|
+
chunk_type: d.chunk_type,
|
|
1348
|
+
macro: d.macro,
|
|
1349
|
+
stability: d.stability,
|
|
1350
|
+
}
|
|
1351
|
+
})
|
|
1352
|
+
|
|
1353
|
+
return { results: matches, count: matches.length }
|
|
1354
|
+
}
|
|
1355
|
+
// CIL[END chunk_id="agent-runner_executecilchunklookup_1784488696"]
|
|
1356
|
+
// CIL[BEGIN chunk_id="agent-runner_executecilgetcontract_1784488696" type="function" identity="executeCilGetContract" parent="agent-runner_ciltools_1784488696"]
|
|
1357
|
+
// CIL[DOC-MACRO purpose="Tool handler: cil_get_contract"]
|
|
1358
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Return the full contract and metadata for a CIL chunk"]
|
|
1359
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files="custom/apps/cortex/functions/custom_cil-agent.ts"]
|
|
1360
|
+
// CIL[DOC-CONTRACT inputs="params: { chunk_id }\nctx: CoreContext" outputs="chunk contract object" errors="throws if chunk not found" side_effects="DB read on items table"]
|
|
1361
|
+
// CIL[DOC-MICRO logic_summary="Fetches a cil_chunk item by UUID and returns its contract fields" notable_decisions="[]"]
|
|
1362
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
1363
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
1364
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
1365
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeToolAction. Direct callees: adminDb(.framework/functions/_shared/db.ts). Related files: custom/apps/cortex/functions/custom_cil-agent.ts." status="ready"]
|
|
1366
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeToolAction. Total affected: 1." status="ready"]
|
|
1367
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
1368
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeToolAction] and callees [adminDb(.framework/functions/_shared/db.ts)] shape downstream behavior." status="ready"]
|
|
1369
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
1370
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1371
|
+
async function executeCilGetContract(params: any, ctx: CoreContext): Promise<any> {
|
|
1372
|
+
const { chunk_id } = params
|
|
1373
|
+
if (!chunk_id) throw new Error('chunk_id is required')
|
|
1374
|
+
|
|
1375
|
+
const { data, error } = await adminDb
|
|
1376
|
+
.from('items')
|
|
1377
|
+
.select('id, title, data')
|
|
1378
|
+
.eq('id', chunk_id)
|
|
1379
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
1380
|
+
.eq('account_id', ctx.accountId)
|
|
1381
|
+
.single()
|
|
1382
|
+
|
|
1383
|
+
if (error || !data) throw new Error(`CIL chunk not found: ${chunk_id}`)
|
|
1384
|
+
|
|
1385
|
+
const d = data.data || {}
|
|
1386
|
+
return {
|
|
1387
|
+
chunk_id: data.id,
|
|
1388
|
+
chunk_name: d.chunk_name,
|
|
1389
|
+
chunk_type: d.chunk_type,
|
|
1390
|
+
file_path: d.file_path,
|
|
1391
|
+
macro: d.macro,
|
|
1392
|
+
responsibility: d.responsibility,
|
|
1393
|
+
contract: {
|
|
1394
|
+
inputs: d.input_spec,
|
|
1395
|
+
outputs: d.output_spec,
|
|
1396
|
+
errors: d.error_spec,
|
|
1397
|
+
side_effects: d.side_effects,
|
|
1398
|
+
},
|
|
1399
|
+
micro: d.micro,
|
|
1400
|
+
stability: d.stability,
|
|
1401
|
+
confidence: d.confidence,
|
|
1402
|
+
audience: d.audience,
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
// CIL[END chunk_id="agent-runner_executecilgetcontract_1784488696"]
|
|
1406
|
+
// CIL[BEGIN chunk_id="agent-runner_executecilblastradius_1784488696" type="function" identity="executeCilBlastRadius" parent="agent-runner_ciltools_1784488696"]
|
|
1407
|
+
// CIL[DOC-MACRO purpose="Tool handler: cil_blast_radius"]
|
|
1408
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Find chunks transitively linked to a given chunk"]
|
|
1409
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files="custom/apps/cortex/functions/custom_cil-callmap.ts"]
|
|
1410
|
+
// CIL[DOC-CONTRACT inputs="params: { chunk_id, max_depth? }\nctx: CoreContext" outputs="{ chunk_id, max_depth, affected }" errors="throws on DB failure" side_effects="DB reads on link_types and links tables"]
|
|
1411
|
+
// CIL[DOC-MICRO logic_summary="Resolves CIL link type IDs, performs BFS over links, and joins chunk metadata" notable_decisions="[]"]
|
|
1412
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
1413
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
1414
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
1415
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeToolAction. Direct callees: adminDb(.framework/functions/_shared/db.ts). Related files: custom/apps/cortex/functions/custom_cil-callmap.ts." status="ready"]
|
|
1416
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeToolAction. Total affected: 1." status="ready"]
|
|
1417
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
1418
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeToolAction] and callees [adminDb(.framework/functions/_shared/db.ts)] shape downstream behavior." status="ready"]
|
|
1419
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
1420
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1421
|
+
async function executeCilBlastRadius(params: any, ctx: CoreContext): Promise<any> {
|
|
1422
|
+
const { chunk_id, max_depth = 3 } = params
|
|
1423
|
+
if (!chunk_id) throw new Error('chunk_id is required')
|
|
1424
|
+
|
|
1425
|
+
const maxDepth = Math.min(parseInt(max_depth, 10) || 3, 6)
|
|
1426
|
+
|
|
1427
|
+
const { data: linkTypes, error: ltError } = await adminDb
|
|
1428
|
+
.from('link_types')
|
|
1429
|
+
.select('id, slug')
|
|
1430
|
+
.eq('account_id', ctx.accountId)
|
|
1431
|
+
.in('slug', ['cil_imports', 'cil_calls', 'cil_exports', 'cil_tested_by'])
|
|
1432
|
+
|
|
1433
|
+
if (ltError) throw ltError
|
|
1434
|
+
const linkTypeIds = (linkTypes || []).map((lt: any) => lt.id)
|
|
1435
|
+
if (linkTypeIds.length === 0) return { chunk_id, max_depth: maxDepth, affected: [] }
|
|
1436
|
+
|
|
1437
|
+
const visited = new Set<string>()
|
|
1438
|
+
const affected: any[] = []
|
|
1439
|
+
const frontier: { id: string; depth: number }[] = [{ id: chunk_id, depth: 0 }]
|
|
1440
|
+
|
|
1441
|
+
while (frontier.length > 0) {
|
|
1442
|
+
const current = frontier.shift()!
|
|
1443
|
+
if (visited.has(current.id)) continue
|
|
1444
|
+
visited.add(current.id)
|
|
1445
|
+
|
|
1446
|
+
const [sourceLinks, targetLinks] = await Promise.all([
|
|
1447
|
+
adminDb
|
|
1448
|
+
.from('links')
|
|
1449
|
+
.select('id, source_id, target_id, link_type_id, data')
|
|
1450
|
+
.eq('source_id', current.id)
|
|
1451
|
+
.in('link_type_id', linkTypeIds),
|
|
1452
|
+
adminDb
|
|
1453
|
+
.from('links')
|
|
1454
|
+
.select('id, source_id, target_id, link_type_id, data')
|
|
1455
|
+
.eq('target_id', current.id)
|
|
1456
|
+
.in('link_type_id', linkTypeIds),
|
|
1457
|
+
])
|
|
1458
|
+
|
|
1459
|
+
const allLinks = [...(sourceLinks.data || []), ...(targetLinks.data || [])]
|
|
1460
|
+
for (const link of allLinks) {
|
|
1461
|
+
const neighborId = link.source_id === current.id ? link.target_id : link.source_id
|
|
1462
|
+
if (visited.has(neighborId)) continue
|
|
1463
|
+
affected.push({
|
|
1464
|
+
chunk_id: neighborId,
|
|
1465
|
+
link_type_id: link.link_type_id,
|
|
1466
|
+
depth: current.depth + 1,
|
|
1467
|
+
})
|
|
1468
|
+
if (current.depth + 1 < maxDepth) {
|
|
1469
|
+
frontier.push({ id: neighborId, depth: current.depth + 1 })
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
const chunkIds = affected.map(a => a.chunk_id)
|
|
1475
|
+
const { data: items } = await adminDb
|
|
1476
|
+
.from('items')
|
|
1477
|
+
.select('id, title, data')
|
|
1478
|
+
.in('id', chunkIds)
|
|
1479
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
1480
|
+
.eq('account_id', ctx.accountId)
|
|
1481
|
+
|
|
1482
|
+
const itemMap = new Map((items || []).map((i: any) => [i.id, i]))
|
|
1483
|
+
|
|
1484
|
+
return {
|
|
1485
|
+
chunk_id,
|
|
1486
|
+
max_depth: maxDepth,
|
|
1487
|
+
affected: affected.map((a: any) => {
|
|
1488
|
+
const item = itemMap.get(a.chunk_id)
|
|
1489
|
+
const d = item?.data || {}
|
|
1490
|
+
return {
|
|
1491
|
+
...a,
|
|
1492
|
+
chunk_name: d.chunk_name,
|
|
1493
|
+
file_path: d.file_path,
|
|
1494
|
+
link_slug: linkTypes?.find((lt: any) => lt.id === a.link_type_id)?.slug,
|
|
1495
|
+
}
|
|
1496
|
+
}),
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
// CIL[END chunk_id="agent-runner_executecilblastradius_1784488696"]
|
|
1500
|
+
// CIL[BEGIN chunk_id="agent-runner_executecilhashvalidate_1784488696" type="function" identity="executeCilHashValidate" parent="agent-runner_ciltools_1784488696"]
|
|
1501
|
+
// CIL[DOC-MACRO purpose="Tool handler: cil_hash_validate"]
|
|
1502
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Compare a CIL chunk's stored hash to its stored code_content"]
|
|
1503
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/mcp/tools/cil/validate.ts"]
|
|
1504
|
+
// CIL[DOC-CONTRACT inputs="params: { chunk_id }\nctx: CoreContext" outputs="{ chunk_id, stored_hash, computed_hash, is_current, hash_updated_at }" errors="throws if chunk not found" side_effects="DB read on items table"]
|
|
1505
|
+
// CIL[DOC-MICRO logic_summary="Fetches cil_chunk by UUID, normalises its code_content, computes hash, and compares to stored_hash" notable_decisions="[]"]
|
|
1506
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
1507
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
1508
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
1509
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeToolAction. Direct callees: adminDb(.framework/functions/_shared/db.ts). Related files: .framework/mcp/tools/cil/validate.ts." status="ready"]
|
|
1510
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeToolAction. Total affected: 1." status="ready"]
|
|
1511
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
1512
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeToolAction] and callees [adminDb(.framework/functions/_shared/db.ts)] shape downstream behavior." status="ready"]
|
|
1513
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
1514
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1515
|
+
async function executeCilHashValidate(params: any, ctx: CoreContext): Promise<any> {
|
|
1516
|
+
const { chunk_id } = params
|
|
1517
|
+
if (!chunk_id) throw new Error('chunk_id is required')
|
|
1518
|
+
|
|
1519
|
+
const { data, error } = await adminDb
|
|
1520
|
+
.from('items')
|
|
1521
|
+
.select('id, title, data')
|
|
1522
|
+
.eq('id', chunk_id)
|
|
1523
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
1524
|
+
.eq('account_id', ctx.accountId)
|
|
1525
|
+
.single()
|
|
1526
|
+
|
|
1527
|
+
if (error || !data) throw new Error(`CIL chunk not found: ${chunk_id}`)
|
|
1528
|
+
|
|
1529
|
+
const d = data.data || {}
|
|
1530
|
+
const codeContent = d.code_content || ''
|
|
1531
|
+
const storedHash = d.stored_hash
|
|
1532
|
+
const computedHash = computeCilChunkHash(codeContent)
|
|
1533
|
+
|
|
1534
|
+
return {
|
|
1535
|
+
chunk_id,
|
|
1536
|
+
stored_hash: storedHash,
|
|
1537
|
+
computed_hash: computedHash,
|
|
1538
|
+
is_current: storedHash === computedHash,
|
|
1539
|
+
hash_updated_at: d.hash_updated_at,
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
// CIL[END chunk_id="agent-runner_executecilhashvalidate_1784488696"]
|
|
1543
|
+
// CIL[BEGIN chunk_id="agent-runner_executecilprojectstatus_1784488696" type="function" identity="executeCilProjectStatus" parent="agent-runner_ciltools_1784488696"]
|
|
1544
|
+
// CIL[DOC-MACRO purpose="Tool handler: cil_project_status"]
|
|
1545
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Return CIL pipeline status for a project"]
|
|
1546
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files="custom/apps/cortex/functions/custom_cil-project.ts"]
|
|
1547
|
+
// CIL[DOC-CONTRACT inputs="params: { project_id }\nctx: CoreContext" outputs="{ project_id, title, file_count, chunk_count, embedding_coverage_pct, doc_coverage_pct, last_ingested_at }" errors="throws if project not found" side_effects="DB reads on items table"]
|
|
1548
|
+
// CIL[DOC-MICRO logic_summary="Fetches the cil_project item; if summary fields are missing it computes live counts from related cil_file and cil_chunk items" notable_decisions="[]"]
|
|
1549
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
1550
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
1551
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
1552
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: executeToolAction. Direct callees: adminDb(.framework/functions/_shared/db.ts). Related files: custom/apps/cortex/functions/custom_cil-project.ts." status="ready"]
|
|
1553
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: executeToolAction. Total affected: 1." status="ready"]
|
|
1554
|
+
// CIL[DOC-VECTOR type="configuration" content="Config dependencies: none. Test paths: none." status="ready"]
|
|
1555
|
+
// CIL[DOC-VECTOR type="constraint" content="Driven by chunk inputs; callers [executeToolAction] and callees [adminDb(.framework/functions/_shared/db.ts)] shape downstream behavior." status="ready"]
|
|
1556
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
1557
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1558
|
+
async function executeCilProjectStatus(params: any, ctx: CoreContext): Promise<any> {
|
|
1559
|
+
const { project_id } = params
|
|
1560
|
+
if (!project_id) throw new Error('project_id is required')
|
|
1561
|
+
|
|
1562
|
+
const { data: project, error: projectError } = await adminDb
|
|
1563
|
+
.from('items')
|
|
1564
|
+
.select('id, title, data')
|
|
1565
|
+
.eq('id', project_id)
|
|
1566
|
+
.filter('data->>kb_type', 'eq', 'cil_project')
|
|
1567
|
+
.eq('account_id', ctx.accountId)
|
|
1568
|
+
.single()
|
|
1569
|
+
|
|
1570
|
+
if (projectError || !project) throw new Error(`CIL project not found: ${project_id}`)
|
|
1571
|
+
|
|
1572
|
+
const d = project.data || {}
|
|
1573
|
+
|
|
1574
|
+
if (d.file_count !== undefined && d.chunk_count !== undefined) {
|
|
1575
|
+
return {
|
|
1576
|
+
project_id,
|
|
1577
|
+
title: project.title,
|
|
1578
|
+
file_count: d.file_count,
|
|
1579
|
+
chunk_count: d.chunk_count,
|
|
1580
|
+
embedding_coverage_pct: d.embedding_coverage_pct ?? 0,
|
|
1581
|
+
doc_coverage_pct: d.doc_coverage_pct ?? 0,
|
|
1582
|
+
last_ingested_at: d.last_ingested_at,
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
const [fileRes, chunkRes] = await Promise.all([
|
|
1587
|
+
adminDb
|
|
1588
|
+
.from('items')
|
|
1589
|
+
.select('*', { count: 'exact' })
|
|
1590
|
+
.filter('data->>project_id', 'eq', project_id)
|
|
1591
|
+
.filter('data->>kb_type', 'eq', 'cil_file')
|
|
1592
|
+
.eq('is_active', true),
|
|
1593
|
+
adminDb
|
|
1594
|
+
.from('items')
|
|
1595
|
+
.select('*', { count: 'exact' })
|
|
1596
|
+
.filter('data->>project_id', 'eq', project_id)
|
|
1597
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
1598
|
+
.eq('is_active', true),
|
|
1599
|
+
])
|
|
1600
|
+
|
|
1601
|
+
return {
|
|
1602
|
+
project_id,
|
|
1603
|
+
title: project.title,
|
|
1604
|
+
file_count: fileRes.count || 0,
|
|
1605
|
+
chunk_count: chunkRes.count || 0,
|
|
1606
|
+
embedding_coverage_pct: d.embedding_coverage_pct ?? 0,
|
|
1607
|
+
doc_coverage_pct: d.doc_coverage_pct ?? 0,
|
|
1608
|
+
last_ingested_at: d.last_ingested_at,
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
// CIL[END chunk_id="agent-runner_executecilprojectstatus_1784488696"]
|
|
1612
|
+
// CIL[BEGIN chunk_id="agent-runner_executesearchlogs_1784488696" type="function" identity="executeSearchLogs" parent="none"]
|
|
1613
|
+
// CIL[DOC-MACRO purpose="Tool handler: search_logs for runtime and trace logs"]
|
|
1614
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Query public.logs for the current account, optionally filtered by level and message substring"]
|
|
1615
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/migrations/005_core_observability.sql"]
|
|
1616
|
+
// CIL[DOC-CONTRACT inputs="params: { q, level, limit? }\nctx: CoreContext" outputs="{ results }" errors="throws on DB failure" side_effects="DB read on logs table"]
|
|
1617
|
+
// CIL[DOC-MICRO logic_summary="Text-search recent logs scoped to account, ordered by created_at desc" notable_decisions="[]"]
|
|
1618
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.85" flags="none"]
|
|
1619
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
1620
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
1621
|
+
async function executeSearchLogs(params: any, ctx: CoreContext): Promise<any> {
|
|
1622
|
+
const { q, level, limit = 20 } = params
|
|
1623
|
+
let query = adminDb
|
|
1624
|
+
.from('logs')
|
|
1625
|
+
.select('id, app_id, type_id, level, message, context, metadata, created_at')
|
|
1626
|
+
.eq('account_id', ctx.accountId)
|
|
1627
|
+
.order('created_at', { ascending: false })
|
|
1628
|
+
.limit(limit)
|
|
1629
|
+
if (level) query = query.eq('level', level)
|
|
1630
|
+
if (q) query = query.or(`message.ilike.%${q}%,context->>trace.ilike.%${q}%`)
|
|
1631
|
+
const { data, error } = await query
|
|
1632
|
+
if (error) throw error
|
|
1633
|
+
return { results: data || [], count: (data || []).length }
|
|
1634
|
+
}
|
|
1635
|
+
// CIL[END chunk_id="agent-runner_executesearchlogs_1784488696"]
|
|
1636
|
+
// CIL[BEGIN chunk_id="agent-runner_executesearchobservability_1784488696" type="function" identity="executeSearchObservability" parent="none"]
|
|
1637
|
+
// CIL[DOC-MACRO purpose="Tool handler: search_observability for metrics and traces"]
|
|
1638
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Query public.observability for the current account, optionally filtered by metric name"]
|
|
1639
|
+
// CIL[DOC-CONTEXT module="_shared/agent-runner" layer="infrastructure" related_files=".framework/migrations/005_core_observability.sql"]
|
|
1640
|
+
// CIL[DOC-CONTRACT inputs="params: { metric_name, limit? }\nctx: CoreContext" outputs="{ results }" errors="throws on DB failure" side_effects="DB read on observability table"]
|
|
1641
|
+
// CIL[DOC-MICRO logic_summary="Fetch recent observability metrics scoped to account, ordered by created_at desc" notable_decisions="[]"]
|
|
1642
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.85" flags="none"]
|
|
1643
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
1644
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
1645
|
+
async function executeSearchObservability(params: any, ctx: CoreContext): Promise<any> {
|
|
1646
|
+
const { metric_name, limit = 20 } = params
|
|
1647
|
+
let query = adminDb
|
|
1648
|
+
.from('observability')
|
|
1649
|
+
.select('id, app_id, type_id, metric_name, metric_value, metric_unit, context, metadata, created_at')
|
|
1650
|
+
.eq('account_id', ctx.accountId)
|
|
1651
|
+
.order('created_at', { ascending: false })
|
|
1652
|
+
.limit(limit)
|
|
1653
|
+
if (metric_name) query = query.eq('metric_name', metric_name)
|
|
1654
|
+
const { data, error } = await query
|
|
1655
|
+
if (error) throw error
|
|
1656
|
+
return { results: data || [], count: (data || []).length }
|
|
1323
1657
|
}
|
|
1658
|
+
// CIL[END chunk_id="agent-runner_executesearchobservability_1784488696"]
|