npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-plugin-tool-image

v0.3.1

Published

Consumer for image generation in the DeepSeek Harness: the tool the model sees, the route gate, batch admission, and the replayable projection

Readme

dsh-plugin-tool-image

Consumer de geração de imagem para o DeepSeek Harness.

É a tool que o modelo enxerga. Pede imagens pelo vocabulário de dsh-plugin-image, publica o resultado no armazenamento de anexos do deployment, e devolve ao turno blocos de imagem ou as referências dos anexos, conforme o deployment escolher. Não sabe qual provedor está montado, e não pode saber: se algum nome desta superfície passar a dizer como os bytes viajaram, scripts/check-no-secrets.mjs falha.

Montagem

# cordis.patch.yml do profile
plugins:
  dsh-plugin-tool-image:
    models:
      - google/gemini-2.5-flash-image
    returnMode: model-visible

| Campo | Obrigatório | Padrão | O que é | |---|---|---|---| | models | não | ausente | Enumeração que o modelo enxerga no argumento model. Não vazia e sem repetição quando presente | | returnMode | não | model-visible | Como o lote produzido volta ao turno. União fechada de dois valores |

Sem models, o argumento model não é declarado e o provedor usa o padrão configurado nele. Isso é a ausência de um argumento opcional, visível no esquema que o modelo recebe, e não um fallback escondido.

returnMode

| Valor | Consulta a rota da sessão? | O que volta ao turno | |---|---|---| | model-visible | sim, antes de qualquer gasto | um bloco de imagem por imagem produzida | | reference-only | não | um bloco de texto com os identificadores dos anexos |

O lote é arquivado nos dois modos, inteiro e na ordem do provedor. O que muda é a projeção.

model-visible é o padrão e é o comportamento anterior a este campo: se a rota que serve a conversa não declara a modalidade image, a chamada é recusada com IMAGE_ROUTE_TEXT_ONLY ou IMAGE_ROUTE_UNKNOWN antes da chamada paga. A recusa protege uma promessa concreta — a de devolver conteúdo que o modelo consegue ler.

reference-only existe porque essa promessa nem sempre é a desejada. Um deployment que quer gerar e arquivar, sem que o modelo enxergue o resultado, não tem por que amarrar a capacidade ao modelo que serve a conversa. Nesse modo a rota não é consultada, e os dois códigos de recusa acima deixam de ser alcançáveis — continuam existindo e exportados, para o outro modo.

Um valor fora dos dois faz o carregamento falhar com TypeError nomeando returnMode, antes de qualquer chamada.

O modo com que uma operação foi atendida é gravado no valor canônico, ao lado de model, provider e cost. É o que faz uma sessão arquivada ser reexibida como foi gravada, mesmo que o profile mude de modo depois: a projeção lê o valor, nunca a configuração corrente.

Carregar não toca a rede e não lê ambiente. A única coisa que acontece é a validação da configuração e o registro de dois efeitos — o observador de rota e a própria tool. Os dois disposers seguem o fiber que carregou o plugin.

inject declara tools, attachments e llm. image não está lá, e isso é decisão. Declará-lo impediria a montagem enquanto nenhum provedor estivesse montado, e quem chamasse a tool veria capacidade ausente em vez da recusa nomeada que o contrato promete.

O que o modelo vê

| Argumento | Tipo | Obrigatório | Restrição | |---|---|---|---| | prompt | string | sim | não vazio depois de trim — checado no corpo | | model | string com enum | não | membro da enumeração — checado pelo harness | | aspect_ratio | string | não | livre; o provedor valida contra o catálogo | | n | integer | não | >= 1 e <= o limite de imagens por mensagem do deployment |

aspect_ratio fica livre de propósito: o domínio dele é por modelo e vem do catálogo, que é rede. Declará-lo como enumeração exigiria escolher um domínio no carregamento; deixá-lo livre faz o provedor recusar nomeando o parâmetro, que é o que a fase anterior já entrega.

A ordem, e por que ela é o coração do pacote

1. checagem de corpo   -> IMAGE_TOOL_ARGS | IMAGE_BATCH_TOO_LARGE
2. gate de rota        -> IMAGE_ROUTE_UNKNOWN | IMAGE_ROUTE_TEXT_ONLY
3. resolução da spec   -> ImageRequest completo
4. chamada ao seam     -> o único ponto que custa dinheiro
5. admissão            -> lote inteiro, ordem preservada
6. projeção            -> o valor canônico

Toda recusa evitável acontece antes do passo 4, e cada uma nomeia sua causa. Trocar a ordem manteria os testes do caminho feliz verdes e perderia exatamente a propriedade que este pacote existe para entregar.

Não há passo de decodificação. Ele vive no provedor (ADR-0012): o contrato entrega Uint8Array, e base64 é forma de transporte.

O gate de rota, e o risco que ele carrega

O harness recusa a leitura de imagem quando a rota de modelo ativa não declara image nas modalidades de entrada. A função que faz isso não é exportada pelo contrato público do pacote que a contém, então aqui ela é reimplementada (ADR-0007).

A rota ativa é observada no waterfall llm/stream, chaveada pela sessão do agente que chamou. Requisições com purpose presente são ignoradas: compactação e título de sessão são chamadas auxiliares, e a rota delas não é a rota do turno.

Três recusas, com causas distintas:

| Situação | Código | |---|---| | nenhuma rota observada, ou chamada sem agente | IMAGE_ROUTE_UNKNOWN | | a rota não declara modalidade alguma | IMAGE_ROUTE_UNKNOWN | | a rota declara modalidades e image não está entre elas | IMAGE_ROUTE_TEXT_ONLY |

Capacidade desconhecida recusa. Tratá-la como capacidade presente é exatamente a falha que o gate existe para evitar.

Risco conhecido: se o harness mudar a forma de resolveModelInfo, este gate diverge em silêncio. Duas verificações existem para que isso falhe alto — o pino nominal em tests/published-surface/surface.ts e o teste estrutural em test/surface-drift.test.ts. A primeira prova que o símbolo ainda existe assim; a segunda prova que o gate ainda o usa assim.

Admissão

O lote inteiro vai numa única chamada a AttachmentStore.saveImages, com a ordem preservada. A semântica de tudo-ou-nada vem de lá, e não é reimplementada aqui: falha de validação não inicia escrita alguma, e falha de armazenamento não devolve referência parcial. Um laço sobre saveImage substituiria essa garantia pelo oposto.

admitEncodedImages existe no alvo e não é usada. Ela aceita base64 vindo da wire e então delega a saveImages; como o contrato entrega bytes, alcançá-la exigiria codificar apenas para que ela decodificasse de volta (ADR-0012). É escolha de desenho registrada, não ausência de API.

Um lote maior que o pedido não é compensado aqui: é obrigação declarada do provedor, e compensá-la esconderia o defeito lá. O lote chega inteiro ao armazenamento e é recusado pela própria política quando ultrapassa o limite.

Custo, provedor e modelo

O valor canônico da tool é { images: [{ attachmentId, mediaType, bytes, width, height, name? }], model, provider?, cost? }. O núcleo o persiste junto do resultado, e o replay o relê. Ler custo, provedor e modelo é ler campo, nunca interpretar prosa (ADR-0008).

Ausência é ausência de campo, nunca 0 nem "".

Consequência aceita: custo de imagem não agrega em nenhum totalizador do harness. Somar exige varrer os resultados de tool — e é justamente por isso que os campos existem.

Apresentação

Card generic, sem locations (ADR-0006). A união publicada oferece generic, terminal, diff, search, read e web, e nenhum é card de imagem; locations aponta para arquivos que a tool toca, e esta não toca nenhum — o anexo é endereçado por conteúdo.

As funções de apresentação são puras nos argumentos e totais. Elas rodam ao vivo e de novo no replay do log, então um valor lido de qualquer outro lugar renderizaria de um jeito agora e de outro depois, e um caminho que lance quebraria a renderização de uma sessão antiga inteira.

Falhas

Uma falha que já tinha código a montante mantém o dela: imageFailureCode(kind) para falha do contrato, AttachmentErrorCode para falha do armazenamento. Inventar código novo a cada camada tornaria a causa real irrastreável depois de um salto.

Os cinco códigos próprios estão em IMAGE_TOOL_ERROR_CODES, exportado para que um programa importe a constante em vez de repetir o literal.

Dependências

dependencies é vazio, por decisão. Os pacotes do harness são peerDependencies com intervalo mais devDependencies pinadas em versão exata (ADR-0011): o intervalo deixa a instalação prover versão mais nova sem recompilar, e o pino faz o compilador enxergar só o alvo.

Uma nota de implementação

Nenhum estado de classe deste repositório vive em campo privado de ECMAScript (#x). Aqui a razão não é o proxy de contexto — este plugin não é lido por um — e sim manter a regra uniforme, o que custa zero e remove a pergunta de qual classe é lida assim.

Uma nota relacionada, e essa é de plataforma: findImageService do contrato usa ctx.get('image') e não ctx.image. O acessador de propriedade passa pelo proxy de contexto, que recusa um nome que o fiber chamador não declarou em inject — ele lança cannot get property "image" without inject em vez de responder undefined. Medido nesta fase; a saída está em specs/003-consumer-admission-bundle/verification-evidence.md.