Skip to content

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 in backend/src/services/toolExecutor.service.ts only checks:
    • allowedProtocols
    • self_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):

  1. Extend ToolExecutionContext to include an invocation source and an explicit selection signal:

    • invocationSource: "agent" | "user" (required at call sites)
    • explicitlySelectedToolIds?: ToolId[] (optional)
  2. Enforce:

    • If policy is explicit_user_selected and invocationSource === "agent":
      • deny unless explicitlySelectedToolIds contains toolId
    • If invocationSource === "user":
      • allow (this is explicit by construction), still respecting protocol allowlist + enablement.

Call-site expectations (to keep system coherent)

  • LangChain tool calling (backend/src/services/langchain/langchainToolTurn.service.ts) must pass invocationSource: "agent" and (for now) no explicit tool allowlist → restricted tools are denied.
  • Direct tool execution REST (backend/src/controllers/tool.controller.tsexecuteTool) should pass invocationSource: "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 explicitlySelectedToolIds as 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_selected cannot 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

  1. If a tool exists at runtime, it must exist in the canonical tool catalog.
  2. 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_facts to backend/src/config/toolCatalog.ts
    • category: system or memory
    • access: default self_only (user facts only) + protocol allowlist (e.g. private/system)
    • enabled: true, but controllable by enablement overrides
  • 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 ChatWindow call sendMessage() even when group.
  • Group socket send drops preferences even though payload is constructed upstream.
  • Group optimistic UI branch is empty; error handling lacks try/catch/finally → stuck thinking.
  • ToolProgressPanel reads single status/progress only.
  • Env mismatch: some functions use VITE_API_URL, others use VITE_API_BASE_URL.

Target behavior

  1. If chat is group, all sends and history updates use group pipeline.
  2. Payload parity: if preferences exist for single, they exist for group or are explicitly unsupported.
  3. Failure cannot leave UI stuck.
  4. Tool progress/typing indicators use the correct status source.

Implementation plan (PR-sized)

  1. ChatWindow.tsx
    • Make suggested prompt send function depend on isGroup (store should expose sendGroupMessage or a unified sendChat({chatType,...})).
    • Render single-only UI (choices/tool progress) only when applicable.
  2. 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.
  3. groupSocketServices.ts
    • Expand payload type to include preferences + timezone/locale if supported by backend group chat.
  4. API env variables
    • Unify VITE_API_URL vs VITE_API_BASE_URL usage (pick one and standardize).

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.tsx
  • aso-portal/src/components/chat/ToolProgressPanel.tsx
  • aso-portal/src/stores/utils/messageSendUtils.ts
  • aso-portal/src/services/groupSocketServices.ts
  • aso-portal/src/api/asoApi.ts and aso-portal/src/api/axiosClient.ts

ASO Universal Consciousness System Documentation