// Force strict mode and setup for ESM
"use strict";
import {
  BaseDeclarativeTool,
  BaseToolInvocation,
  ToolDisplayNames,
  ToolNames
} from "./chunk-MM7TH7ZM.js";
import {
  init_esbuild_shims
} from "./chunk-5O2XNYP6.js";
import {
  __name
} from "./chunk-J2S4EL5Y.js";

// packages/core/src/tools/record-artifact.ts
init_esbuild_shims();
import path from "node:path";
var DESCRIPTION = `Registers a session artifact so clients can show it in an artifacts panel. Use it after creating a useful file, URL, image, report, notebook, or other intermediate result that the user may want to open later, unless the producing tool already returned artifact metadata. For example, write_file automatically records HTML, image, PDF, and notebook files it writes inside the workspace, so do not call record_artifact again for the same workspacePath; still call it for other formats such as Markdown, CSV, JSON, and plain text, and for files produced outside write_file.

This tool only records metadata. It does not publish, upload, read, write, or verify the referenced resource. Provide exactly one locator: workspacePath, managedId, or url. Use the Artifact tool, not record_artifact, for published interactive HTML artifacts.`;
var ARTIFACT_TITLE_MAX_LENGTH = 200;
var ARTIFACT_WORKSPACE_PATH_MAX_LENGTH = 500;
var RecordArtifactInvocation = class extends BaseToolInvocation {
  static {
    __name(this, "RecordArtifactInvocation");
  }
  getDescription() {
    return `Recording artifact ${this.params.title}`;
  }
  execute(_signal) {
    const artifact = {
      title: this.params.title.trim(),
      kind: this.params.kind,
      storage: this.params.storage ?? inferStorage(this.params),
      description: trimOptional(this.params.description),
      workspacePath: trimOptional(this.params.workspacePath),
      managedId: trimOptional(this.params.managedId),
      url: trimOptional(this.params.url),
      mimeType: trimOptional(this.params.mimeType),
      sizeBytes: this.params.sizeBytes,
      metadata: this.params.metadata
    };
    return Promise.resolve({
      llmContent: `Recorded artifact "${artifact.title}".`,
      returnDisplay: `Recorded artifact **${artifact.title}**.`,
      artifacts: [artifact]
    });
  }
};
var RecordArtifactTool = class _RecordArtifactTool extends BaseDeclarativeTool {
  static {
    __name(this, "RecordArtifactTool");
  }
  static Name = ToolNames.RECORD_ARTIFACT;
  constructor() {
    super(
      _RecordArtifactTool.Name,
      ToolDisplayNames.RECORD_ARTIFACT,
      DESCRIPTION,
      "other" /* Other */,
      {
        type: "object",
        properties: {
          title: {
            type: "string",
            description: "Concise title shown in the client artifact list."
          },
          kind: {
            type: "string",
            enum: [
              "file",
              "link",
              "html",
              "image",
              "video",
              "audio",
              "pdf",
              "notebook",
              "other"
            ],
            description: "Best-effort artifact type for client rendering."
          },
          storage: {
            type: "string",
            enum: ["workspace", "external_url", "managed"],
            description: "Storage class. Omit it to infer from the provided locator."
          },
          description: {
            type: "string",
            description: "Optional short description for the user."
          },
          workspacePath: {
            type: "string",
            description: "Workspace-relative path for a file produced in the current workspace."
          },
          managedId: {
            type: "string",
            description: "Opaque identifier for a resource managed by an extension or tool."
          },
          url: {
            type: "string",
            description: "HTTP or HTTPS URL that the user can open for details."
          },
          mimeType: {
            type: "string",
            description: "Optional MIME type."
          },
          sizeBytes: {
            type: "integer",
            minimum: 0,
            description: "Optional size in bytes."
          },
          metadata: {
            type: "object",
            additionalProperties: {
              anyOf: [
                { type: "string" },
                { type: "number" },
                { type: "boolean" },
                { type: "null" }
              ]
            },
            description: "Small primitive metadata bag for client-specific display hints."
          }
        },
        required: ["title"]
      },
      true,
      false,
      true,
      false,
      "artifact url link file image report notebook dashboard"
    );
  }
  validateToolParamValues(params) {
    params.title = (params.title ?? "").trim();
    const titleError = validateString(
      params.title,
      "title",
      ARTIFACT_TITLE_MAX_LENGTH,
      true
    );
    if (titleError) {
      return titleError;
    }
    const descriptionError = validateString(
      params.description,
      "description",
      1e3,
      false
    );
    if (descriptionError) {
      return descriptionError;
    }
    const mimeTypeError = validateString(
      params.mimeType,
      "mimeType",
      120,
      false
    );
    if (mimeTypeError) {
      return mimeTypeError;
    }
    if (params.kind && !isArtifactKind(params.kind)) {
      return '"kind" must be a supported artifact kind';
    }
    if (params.storage && params.storage !== "workspace" && params.storage !== "external_url" && params.storage !== "managed") {
      return '"storage" must be workspace, external_url, or managed';
    }
    const locators = [
      trimOptional(params.workspacePath),
      trimOptional(params.managedId),
      trimOptional(params.url)
    ].filter(Boolean);
    if (locators.length !== 1) {
      return 'Provide exactly one of "workspacePath", "managedId", or "url"';
    }
    const inferredStorage = inferStorage(params);
    if (params.storage && params.storage !== inferredStorage) {
      return `"storage" must be "${inferredStorage}" for the provided locator`;
    }
    if (params.workspacePath) {
      const workspacePathError = validateWorkspacePath(params.workspacePath);
      if (workspacePathError) {
        return workspacePathError;
      }
    }
    if (params.managedId) {
      const managedIdError = validateManagedId(params.managedId);
      if (managedIdError) {
        return managedIdError;
      }
    }
    if (params.url) {
      try {
        const parsed = new URL(params.url);
        if (parsed.username || parsed.password) {
          return '"url" must not include credentials';
        }
        if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
          return '"url" must use http or https';
        }
      } catch {
        return '"url" must be a valid URL';
      }
    }
    if (params.sizeBytes !== void 0 && (!Number.isSafeInteger(params.sizeBytes) || params.sizeBytes < 0)) {
      return '"sizeBytes" must be a non-negative safe integer';
    }
    const metadataError = validateMetadata(params.metadata);
    if (metadataError) {
      return metadataError;
    }
    return null;
  }
  createInvocation(params) {
    return new RecordArtifactInvocation(params);
  }
};
function inferStorage(params) {
  if (trimOptional(params.workspacePath)) {
    return "workspace";
  }
  if (trimOptional(params.managedId)) {
    return "managed";
  }
  return "external_url";
}
__name(inferStorage, "inferStorage");
function trimOptional(value) {
  const trimmed = value?.trim();
  return trimmed ? trimmed : void 0;
}
__name(trimOptional, "trimOptional");
function validateString(value, field, maxLength, required) {
  const trimmed = value?.trim();
  if (!trimmed) {
    return required ? `Missing or empty "${field}"` : null;
  }
  if (trimmed.length > maxLength) {
    return `"${field}" exceeds ${maxLength} characters`;
  }
  if (hasControlCharacter(trimmed, field === "description")) {
    return `"${field}" contains control characters`;
  }
  if (isDisplayField(field) && hasUnsafeDisplayPayload(trimmed)) {
    return `"${field}" contains unsafe markup`;
  }
  return null;
}
__name(validateString, "validateString");
function validateManagedId(value) {
  const trimmed = value.trim();
  const stringError = validateString(trimmed, "managedId", 200, true);
  if (stringError) {
    return stringError;
  }
  if (trimmed.includes("/") || trimmed.includes("\\") || trimmed.includes("..") || path.isAbsolute(trimmed) || path.win32.isAbsolute(trimmed)) {
    return '"managedId" must be an opaque managed resource id';
  }
  return null;
}
__name(validateManagedId, "validateManagedId");
function isDisplayField(field) {
  return field === "title" || field === "description" || field === "mimeType" || field === "workspacePath" || field === "managedId";
}
__name(isDisplayField, "isDisplayField");
function hasControlCharacter(value, allowLineWhitespace = false) {
  for (let i = 0; i < value.length; i++) {
    const code = value.charCodeAt(i);
    if (allowLineWhitespace && (code === 9 || code === 10 || code === 13)) {
      continue;
    }
    if (code <= 31 || code === 127 || code >= 8203 && code <= 8207 || code === 8232 || code === 8233 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297 || code === 65279) {
      return true;
    }
  }
  return false;
}
__name(hasControlCharacter, "hasControlCharacter");
function hasUnsafeDisplayPayload(value) {
  return /<\s*\/?[a-z!]|&(?:#[0-9]+|#x[0-9a-f]+|[a-z][a-z0-9]+);|javascript\s*:|data\s*:\s*(?:text\/(?:html|javascript)|application\/javascript|image\/svg\+xml)/i.test(
    value
  ) || /(?:^|[\s"'`<])on[a-z][a-z0-9-]*\s*=/i.test(value);
}
__name(hasUnsafeDisplayPayload, "hasUnsafeDisplayPayload");
function validateWorkspacePath(value) {
  const trimmed = value.trim();
  const stringError = validateString(
    trimmed,
    "workspacePath",
    ARTIFACT_WORKSPACE_PATH_MAX_LENGTH,
    true
  );
  if (stringError) {
    return stringError;
  }
  if (path.isAbsolute(trimmed) || path.win32.isAbsolute(trimmed) || /^[A-Za-z]:/.test(trimmed)) {
    return '"workspacePath" must be relative to the workspace';
  }
  const portableNormalized = path.posix.normalize(trimmed.replace(/\\/g, "/"));
  if (portableNormalized === ".." || portableNormalized.startsWith("../") || path.posix.isAbsolute(portableNormalized)) {
    return '"workspacePath" must stay inside the workspace';
  }
  return null;
}
__name(validateWorkspacePath, "validateWorkspacePath");
function validateMetadata(metadata) {
  if (metadata === void 0) {
    return null;
  }
  if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata)) {
    return '"metadata" must be an object';
  }
  for (const [key, value] of Object.entries(metadata)) {
    if (!key) {
      return '"metadata" keys must not be empty';
    }
    if (key.length > 120) {
      return '"metadata" keys must be 120 characters or fewer';
    }
    if (hasControlCharacter(key) || hasUnsafeDisplayPayload(key)) {
      return '"metadata" keys contain unsafe content';
    }
    if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
      return '"metadata" values must be primitive';
    }
    if (typeof value === "number" && !Number.isFinite(value)) {
      return '"metadata" numbers must be finite';
    }
    if (typeof value === "string" && (hasControlCharacter(value) || hasUnsafeDisplayPayload(value))) {
      return '"metadata" string values contain unsafe content';
    }
  }
  if (Buffer.byteLength(JSON.stringify(metadata), "utf8") > 4096) {
    return '"metadata" must be 4096 bytes or fewer';
  }
  return null;
}
__name(validateMetadata, "validateMetadata");
function isArtifactKind(kind) {
  return kind === "file" || kind === "link" || kind === "html" || kind === "image" || kind === "video" || kind === "audio" || kind === "pdf" || kind === "notebook" || kind === "other";
}
__name(isArtifactKind, "isArtifactKind");

export {
  ARTIFACT_TITLE_MAX_LENGTH,
  ARTIFACT_WORKSPACE_PATH_MAX_LENGTH,
  RecordArtifactTool,
  hasControlCharacter,
  hasUnsafeDisplayPayload
};
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
