Phase 1 PR Specs — Security and Correctness
Last Updated: 2026-02-28
Content-Type: How-to (implementation-ready PR specs)
Audience: Engineering
PR1.1 — Enforce explicit_user_selected tool policy
Why (problem)
- Tool catalog contains
targetScope: "explicit_user_selected"for restricted observer/admin tools (e.g. user graph and system snapshot tooling), but runtime enforcement inbackend/src/services/toolExecutor.service.tsonly checks:allowedProtocolsself_only
- This creates a false sense of safety: UI and catalog imply a restriction that does not exist.
What we’re standardizing (definition)
targetScope: "explicit_user_selected" means:
- The tool must not be callable by autonomous agent tool calling by default.
- It may be executed only when the user explicitly opted-in for that tool (per request or per session), or when the user triggers a direct tool run endpoint intended for explicit execution.
Proposed minimal enforcement (safe default)
Implement in the centralized executor (executeTool() path):
Extend
ToolExecutionContextto include an invocation source and an explicit selection signal:invocationSource: "agent" | "user"(required at call sites)explicitlySelectedToolIds?: ToolId[](optional)
Enforce:
- If policy is
explicit_user_selectedandinvocationSource === "agent":- deny unless
explicitlySelectedToolIdscontainstoolId
- deny unless
- If
invocationSource === "user":- allow (this is explicit by construction), still respecting protocol allowlist + enablement.
- If policy is
Call-site expectations (to keep system coherent)
- LangChain tool calling (
backend/src/services/langchain/langchainToolTurn.service.ts) must passinvocationSource: "agent"and (for now) no explicit tool allowlist → restricted tools are denied. - Direct tool execution REST (
backend/src/controllers/tool.controller.ts→executeTool) should passinvocationSource: "user"so explicit tool runs still work.
API + UI follow-up (optional for Phase 1, required before enabling in chat)
If you want the agent to be allowed to call explicit tools after a user selection:
- Portal must send
explicitlySelectedToolIdsas part of chat payload (single + group) OR store it per conversation (backend). - Backend must validate it against the active protocol + enablement + policy (no client trust).
Acceptance criteria
- A tool with
explicit_user_selectedcannot be executed from the LangChain agent loop unless explicitly allowed. - The same tool can be executed from the explicit “run tool” endpoint.
- Policy enforcement is test-covered.
File touch list (expected)
backend/src/config/toolCatalog.ts(confirm policy assignments)backend/src/services/toolExecutor.service.ts(enforcement)backend/src/services/langchain/langchainToolTurn.service.ts(context wiring)backend/src/controllers/tool.controller.ts(context wiring)- Tests (location depends on current harness)
Smoke tests
- Attempt restricted observer tool via chat → denied.
- Run restricted observer tool explicitly via tool-run endpoint → allowed (if protocol allowlist allows).
PR1.2 — Remove LangChain-only “hidden tool” bypass
Why (problem)
extract_and_save_user_facts is injected directly into the LangChain tool list and does not flow through:
- tool catalog
- enablement overrides
- access policy overrides
- “AVAILABLE TOOLS” prompt tool list
- normal audit trail assumptions
Standardization rules to enforce
- If a tool exists at runtime, it must exist in the canonical tool catalog.
- Every tool execution must pass through the centralized executor and policy enforcement.
Implementation options
Option A (recommended): Make it a first-class tool
- Add
extract_and_save_user_factstobackend/src/config/toolCatalog.ts- category:
systemormemory - access: default
self_only(user facts only) + protocol allowlist (e.g. private/system) - enabled: true, but controllable by enablement overrides
- category:
- Add schema to
backend/src/services/tools/toolSchemas.ts - Add executor handler in
backend/src/services/toolExecutor.service.ts - Remove custom DynamicStructuredTool injection in
langchainToolTurn.service.ts
Option B: Internal tool with explicit “internal-only” metadata
- Still register in catalog + executor, but exclude from prompt tool definitions by default.
- Requires a documented rule for “internal tools” in
toolCompilation.service.ts.
Acceptance criteria
- The tool is visible in the canonical list (admin UI, docs).
- Enablement/policy overrides apply.
- LangChain calls it via the same executor as other tools.
PR1.3 — Portal group chat correctness + payload parity
Why (problem)
Current UI and store logic contains multiple single-vs-group mismatches:
- Suggested prompts in
ChatWindowcallsendMessage()even when group. - Group socket send drops
preferenceseven though payload is constructed upstream. - Group optimistic UI branch is empty; error handling lacks
try/catch/finally→ stuckthinking. ToolProgressPanelreads single status/progress only.- Env mismatch: some functions use
VITE_API_URL, others useVITE_API_BASE_URL.
Target behavior
- If chat is group, all sends and history updates use group pipeline.
- Payload parity: if preferences exist for single, they exist for group or are explicitly unsupported.
- Failure cannot leave UI stuck.
- Tool progress/typing indicators use the correct status source.
Implementation plan (PR-sized)
ChatWindow.tsx- Make suggested prompt send function depend on
isGroup(store should exposesendGroupMessageor a unifiedsendChat({chatType,...})). - Render single-only UI (choices/tool progress) only when applicable.
- Make suggested prompt send function depend on
messageSendUtils.ts- Wrap upload + send in
try/catch/finally - Implement group optimistic insert (or explicitly remove optimistic behavior for both and rely on server echo).
- Ensure state resets on error.
- Wrap upload + send in
groupSocketServices.ts- Expand payload type to include preferences + timezone/locale if supported by backend group chat.
- API env variables
- Unify
VITE_API_URLvsVITE_API_BASE_URLusage (pick one and standardize).
- Unify
Acceptance criteria
- Suggested prompts work in group chat.
- Preferences are transmitted in group chat (or UI disables them for group).
- Upload failure does not leave state stuck.
- Tool progress panel renders correctly for the current chat type.
File touch list (expected)
aso-portal/src/components/chat/ChatWindow.tsxaso-portal/src/components/chat/ToolProgressPanel.tsxaso-portal/src/stores/utils/messageSendUtils.tsaso-portal/src/services/groupSocketServices.tsaso-portal/src/api/asoApi.tsandaso-portal/src/api/axiosClient.ts