// v1.13.17-cross-repo-reads: tool the model uses to request read access to
// a path outside its session's primary project root. When the model emits
// view_file("/opt/forks/foo/go.mod") under a session scoped to /opt/boocode,
// pathGuard's error message hints at this tool. The model then emits
//   request_read_access(path="/opt/forks/foo/go.mod",
//                       reason="investigating foo to write the design doc")
// The tool's execute does cheap up-front validation: if the requested path
// can't possibly be granted under the current whitelist + repo-shape rules,
// it returns a denial immediately without prompting the user. Otherwise, the
// tool-phase pause branch (parallel of ask_user_input) stores a pending
// sentinel and waits for the user's allow/deny via the grant_read_access
// endpoint.
//
// The execute body never directly mutates state; the grant endpoint owns
// the persistence path. This keeps the tool-side logic side-effect-free
// (it's just a request) and matches ask_user_input's "server-side no-op
// fallback, pause happens in tool-phase" shape.

import { z } from 'zod';
import type { ToolDef } from './tools.js';

const RequestReadAccessInput = z.object({
  path: z.string().min(1),
  reason: z.string().min(1).max(500),
});
type RequestReadAccessInputT = z.infer<typeof RequestReadAccessInput>;

export const requestReadAccess: ToolDef<RequestReadAccessInputT> = {
  name: 'request_read_access',
  description:
    "Ask the user for read-only access to a path outside the current " +
    "session's project scope. Use when a previous read tool (view_file, " +
    'list_dir, grep, find_files) was refused with a path-escapes-project ' +
    'error and the path is plausibly under another known repository (e.g. ' +
    '/opt/forks/foo). Provide a short reason describing why you need the ' +
    "access. Pauses the conversation until the user picks Allow or Deny; " +
    'the next assistant turn sees the result. On Allow, the tool result ' +
    'is "granted: <root>" — subsequent reads under that root succeed for ' +
    'the rest of the session. On Deny, the tool result is "denied". Do ' +
    'not call this for paths that are already inside the project root.',
  inputSchema: RequestReadAccessInput,
  jsonSchema: {
    type: 'function',
    function: {
      name: 'request_read_access',
      description:
        "Ask the user for read-only access to a path outside the session's " +
        'project scope. Pauses the conversation until the user picks Allow ' +
        'or Deny. Subsequent reads under the granted root succeed for the ' +
        'rest of the session.',
      parameters: {
        type: 'object',
        properties: {
          path: {
            type: 'string',
            description:
              'Absolute path the model wants to read. Must be under the ' +
              "server's PROJECT_ROOT_WHITELIST (default /opt) and outside " +
              "the session's primary project root.",
          },
          reason: {
            type: 'string',
            description:
              'Short rationale (<=500 chars) shown to the user explaining ' +
              'why the access is needed. The user uses this to decide.',
          },
        },
        required: ['path', 'reason'],
        additionalProperties: false,
      },
    },
  },
  // Server-side no-op. The "execution" of request_read_access is the
  // pause-and-resume cycle managed by tool-phase.ts + the grant endpoint.
  // The inference loop catches this tool name BEFORE executeToolCall fires
  // and inserts a pending sentinel instead — this fallback only runs if
  // something bypasses that branch, in which case we surface the pending
  // shape so downstream code can still detect it. Mirrors ask_user_input.
  async execute(input) {
    return { _pending: true, path: input.path, reason: input.reason };
  },
};
