From 7b4c1f15e076367d32d41601f27aade7b2b318b6 Mon Sep 17 00:00:00 2001 From: TONresistor <240980241+TONresistor@users.noreply.github.com> Date: Fri, 28 Aug 2026 13:09:52 +0200 Subject: [PATCH 1/4] ci: streamline Docker and release builds --- .github/workflows/ci.yml | 5 ----- .github/workflows/release.yml | 22 ++++------------------ 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 070d91b5..80366f0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,9 +93,6 @@ jobs: - name: Install WebUI dependencies run: cd web && npm ci - - name: Build SDK workspace - run: npm run build -w packages/sdk - - name: Build run: npm run build @@ -121,8 +118,6 @@ jobs: with: context: . push: false - cache-from: type=gha,scope=teleton-docker - cache-to: type=gha,mode=max,scope=teleton-docker # ---- Telegram notification (one summary per push, pushes only) ---- notify: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 43ce252e..4b881e71 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,10 +32,9 @@ jobs: - run: npm ci - run: cd web && npm ci - - run: npm run build -w packages/sdk + - run: npm run build - run: node scripts/check-sdk-package.mjs - run: npm run typecheck - - run: npm run build - run: node dist/cli/index.js --help - name: Run tests @@ -44,12 +43,7 @@ jobs: - name: Enforce SDK coverage run: npm run test:sdk:coverage - - uses: actions/upload-artifact@v7 - with: - name: dist - path: dist/ - - # Verify the image before any registry publication starts. + # Ensure Docker is buildable before publishing immutable npm packages. verify-docker: runs-on: ubuntu-latest timeout-minutes: 30 @@ -62,8 +56,6 @@ jobs: with: context: . push: false - cache-from: type=gha,scope=teleton-docker - cache-to: type=gha,mode=max,scope=teleton-docker # ---- Publish to npm ---- publish-npm: @@ -102,12 +94,6 @@ jobs: - run: cd web && npm ci if: steps.check.outputs.publish == 'true' - - uses: actions/download-artifact@v8 - if: steps.check.outputs.publish == 'true' - with: - name: dist - path: dist/ - - run: npm publish --provenance --access public if: steps.check.outputs.publish == 'true' env: @@ -185,8 +171,8 @@ jobs: with: context: . push: true - cache-from: type=gha,scope=teleton-docker - cache-to: type=gha,mode=max,scope=teleton-docker + cache-from: type=registry,ref=ghcr.io/${{ steps.meta.outputs.repo }}:latest + cache-to: type=inline tags: | ghcr.io/${{ steps.meta.outputs.repo }}:${{ steps.meta.outputs.version }} ghcr.io/${{ steps.meta.outputs.repo }}:latest From 64f0976eca57e97cb27a27167c1c89a04b6c45cc Mon Sep 17 00:00:00 2001 From: TONresistor <240980241+TONresistor@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:04:52 +0200 Subject: [PATCH 2/4] feat(telegram): add inline rich message buttons --- .../messaging/__tests__/send-message.test.ts | 31 ++++++++ .../tools/telegram/messaging/rich-content.ts | 77 +++++++++++++------ .../tools/telegram/messaging/send-message.ts | 2 +- .../__tests__/outgoing-rich-message.test.ts | 30 ++++++++ src/telegram/bridge-interface.ts | 7 +- src/telegram/outgoing-rich-message.ts | 18 +++++ 6 files changed, 140 insertions(+), 25 deletions(-) diff --git a/src/agent/tools/telegram/messaging/__tests__/send-message.test.ts b/src/agent/tools/telegram/messaging/__tests__/send-message.test.ts index 70cfeac2..97c19db0 100644 --- a/src/agent/tools/telegram/messaging/__tests__/send-message.test.ts +++ b/src/agent/tools/telegram/messaging/__tests__/send-message.test.ts @@ -110,6 +110,37 @@ describe("telegram_send_message", () => { }); }); + it("sends small buttons inline with text", async () => { + const rich = { + blocks: [ + { + type: "inline" as const, + items: [ + { type: "text" as const, text: "Open " }, + { + type: "button" as const, + label: "Docs", + action: { type: "url" as const, url: "https://example.com" }, + }, + ], + }, + ], + }; + + const result = await telegramSendMessageExecutor({ rich }, context()); + + expect(result).toMatchObject({ success: true, data: { deliveryKind: "rich" } }); + expect((result.data as { renderedText: string }).renderedText).toBe( + '

Open Docs

' + ); + expect(mocks.sendMessage).toHaveBeenCalledWith({ + chatId: "current", + text: "", + replyToId: undefined, + rich, + }); + }); + it("resolves admin attachment paths before sending", async () => { const result = await telegramSendMessageExecutor( { diff --git a/src/agent/tools/telegram/messaging/rich-content.ts b/src/agent/tools/telegram/messaging/rich-content.ts index 21c08a2b..efb8cdc2 100644 --- a/src/agent/tools/telegram/messaging/rich-content.ts +++ b/src/agent/tools/telegram/messaging/rich-content.ts @@ -22,31 +22,48 @@ const buttonStyleSchema = Type.Union([ Type.Literal("primary"), Type.Literal("success"), Type.Literal("danger"), + Type.Literal("link"), ]); -const buttonSchema = Type.Object( - { - label: Type.String({ minLength: 1, maxLength: 256 }), - action: Type.Union([ - Type.Object( - { - type: Type.Literal("url"), - url: Type.String({ minLength: 1, maxLength: 2048 }), - }, - { additionalProperties: false } - ), - Type.Object( - { - type: Type.Literal("copy"), - text: Type.String({ minLength: 1, maxLength: 256 }), - }, - { additionalProperties: false } - ), - ]), - style: Type.Optional(buttonStyleSchema), - }, - { additionalProperties: false } -); +const buttonFields = { + label: Type.String({ minLength: 1, maxLength: 256 }), + action: Type.Union([ + Type.Object( + { + type: Type.Literal("url"), + url: Type.String({ minLength: 1, maxLength: 2048 }), + }, + { additionalProperties: false } + ), + Type.Object( + { + type: Type.Literal("copy"), + text: Type.String({ minLength: 1, maxLength: 256 }), + }, + { additionalProperties: false } + ), + ]), + style: Type.Optional(buttonStyleSchema), +}; + +const buttonSchema = Type.Object(buttonFields, { additionalProperties: false }); + +const inlineItemSchema = Type.Union([ + Type.Object( + { + type: Type.Literal("text"), + text: Type.String({ minLength: 1, maxLength: RICH_MESSAGE_MAX_BYTES }), + }, + { additionalProperties: false } + ), + Type.Object( + { + type: Type.Literal("button"), + ...buttonFields, + }, + { additionalProperties: false } + ), +]); const buttonRowFields = { align: Type.Optional( @@ -76,6 +93,20 @@ const richBlockSchema = Type.Union([ }, { additionalProperties: false } ), + Type.Object( + { + type: Type.Literal("inline"), + items: Type.Array(inlineItemSchema, { + minItems: 1, + maxItems: RICH_MESSAGE_MAX_BLOCKS, + }), + }, + { + additionalProperties: false, + description: + "One paragraph mixing plain text and small inline URL/copy buttons in exact order. Use link style for a subtle text-like button.", + } + ), Type.Object( { type: Type.Literal("heading"), diff --git a/src/agent/tools/telegram/messaging/send-message.ts b/src/agent/tools/telegram/messaging/send-message.ts index ac46aa8d..4170e292 100644 --- a/src/agent/tools/telegram/messaging/send-message.ts +++ b/src/agent/tools/telegram/messaging/send-message.ts @@ -27,7 +27,7 @@ interface SendMessageParams { export const telegramSendMessageTool: Tool = { name: "telegram_send_message", description: - "Send a Telegram message. Omit chatId to use the current chat. Use text alone for a normal message, or add rich for one native user-mode Rich Message with structured blocks, local attachments, URL/copy buttons, alignment, and styles. Do not write tg:// references yourself.", + "Send a Telegram message. Omit chatId to use the current chat. Use text alone for a normal message, or add rich for one native user-mode Rich Message with structured blocks, local attachments, small inline or row URL/copy buttons, alignment, and styles. Do not write tg:// references yourself.", parameters: Type.Object({ chatId: Type.Optional( Type.String({ diff --git a/src/telegram/__tests__/outgoing-rich-message.test.ts b/src/telegram/__tests__/outgoing-rich-message.test.ts index ed2e457c..9fab1f66 100644 --- a/src/telegram/__tests__/outgoing-rich-message.test.ts +++ b/src/telegram/__tests__/outgoing-rich-message.test.ts @@ -25,6 +25,36 @@ describe("compileRichMessageMarkdown", () => { expect(compiled).toMatchObject({ rtl: true, disableAutoLinks: true }); }); + it("compiles small buttons inline with surrounding text", () => { + const compiled = compileRichMessageMarkdown("", { + blocks: [ + { + type: "inline", + items: [ + { type: "text", text: "Read " }, + { + type: "button", + label: "Docs", + action: { type: "url", url: "https://example.com/docs?a=1&b=2" }, + style: "link", + }, + { type: "text", text: " or copy " }, + { + type: "button", + label: "Ticker", + action: { type: "copy", text: "TON&USD" }, + }, + { type: "text", text: "." }, + ], + }, + ], + }); + + expect(compiled.markdown).toBe( + '

Read Docs or copy Ticker.

' + ); + }); + it("rejects ambiguous simple and advanced layouts", () => { expect(() => compileRichMessageMarkdown("top-level", { diff --git a/src/telegram/bridge-interface.ts b/src/telegram/bridge-interface.ts index 98c64c13..bcb20f18 100644 --- a/src/telegram/bridge-interface.ts +++ b/src/telegram/bridge-interface.ts @@ -58,7 +58,7 @@ export interface RichMessageMediaUpload { caption?: string; } -export type RichMessageButtonStyle = "primary" | "success" | "danger"; +export type RichMessageButtonStyle = "primary" | "success" | "danger" | "link"; export type RichMessageButtonAlignment = "left" | "center" | "right"; export type RichMessageButtonAction = { type: "url"; url: string } | { type: "copy"; text: string }; @@ -69,6 +69,10 @@ export interface RichMessageButton { style?: RichMessageButtonStyle; } +export type RichMessageInlineItem = + | { type: "text"; text: string } + | ({ type: "button" } & RichMessageButton); + export interface RichMessageButtonRow { align?: RichMessageButtonAlignment; buttons: RichMessageButton[]; @@ -76,6 +80,7 @@ export interface RichMessageButtonRow { export type RichMessageBlock = | { type: "paragraph"; markdown: string } + | { type: "inline"; items: RichMessageInlineItem[] } | { type: "heading"; text: string; level?: number } | { type: "quote"; text: string; caption?: string; collapsed?: boolean } | { type: "code"; code: string; language?: string } diff --git a/src/telegram/outgoing-rich-message.ts b/src/telegram/outgoing-rich-message.ts index 2529cda3..6cf35615 100644 --- a/src/telegram/outgoing-rich-message.ts +++ b/src/telegram/outgoing-rich-message.ts @@ -71,6 +71,18 @@ function renderButtonRow(row: RichMessageButtonRow): string { return `\n${row.buttons.map(renderButton).join("\n")}\n`; } +function renderInline(block: Extract): string { + if (block.items.length === 0) throw new Error("Rich Message inline blocks cannot be empty"); + const content = block.items + .map((item) => { + if (item.type === "button") return renderButton(item); + if (!item.text) throw new Error("Rich Message inline text cannot be empty"); + return escapeHtml(item.text).replaceAll("\n", "
"); + }) + .join(""); + return `

${content}

`; +} + function renderAttachment(attachment: RichMessageMediaUpload): string { const label = escapeMarkdownLabel(attachment.caption?.trim() || attachment.type); const title = attachment.caption?.trim() @@ -143,6 +155,8 @@ function renderBlock( switch (block.type) { case "paragraph": return block.markdown; + case "inline": + return renderInline(block); case "heading": { const level = block.level ?? 2; if (!Number.isInteger(level) || level < 1 || level > 6) { @@ -187,6 +201,10 @@ function blockContainsRawMediaReference(block: RichMessageBlock): boolean { switch (block.type) { case "paragraph": return RAW_MEDIA_REFERENCE.test(block.markdown); + case "inline": + return block.items.some( + (item) => item.type === "text" && RAW_MEDIA_REFERENCE.test(item.text) + ); case "heading": return RAW_MEDIA_REFERENCE.test(block.text); case "quote": From 7f4da7277a79228ed8ac8f4ac02b51534cd24b7c Mon Sep 17 00:00:00 2001 From: TONresistor <240980241+TONresistor@users.noreply.github.com> Date: Sat, 5 Sep 2026 20:01:11 +0200 Subject: [PATCH 3/4] feat(models): add Claude Fable 5.1, GPT-6 Astra and Gemini 3.8 Flash --- src/agent/__tests__/model-request.test.ts | 33 +++ src/agent/model-request.ts | 9 + src/config/model-catalog.ts | 32 ++- src/providers/__tests__/codex-models.test.ts | 10 +- src/providers/__tests__/model-catalog.test.ts | 2 +- src/providers/additional-models.ts | 205 ++++++++++++++++++ src/providers/model-resolver.ts | 3 +- 7 files changed, 286 insertions(+), 8 deletions(-) create mode 100644 src/providers/additional-models.ts diff --git a/src/agent/__tests__/model-request.test.ts b/src/agent/__tests__/model-request.test.ts index 9e94b1b8..311c8cc0 100644 --- a/src/agent/__tests__/model-request.test.ts +++ b/src/agent/__tests__/model-request.test.ts @@ -62,6 +62,39 @@ describe("model request preparation", () => { expect(request.options.temperature).toBe(0.4); }); + it.each([ + ["openai", "gpt-6-astra"], + ["openrouter", "anthropic/claude-fable-5.1"], + ["openrouter", "openai/gpt-6-astra"], + ["openrouter", "google/gemini-3.8-flash"], + ])("omits unsupported temperature for %s/%s", (provider, model) => { + const config = AgentConfigSchema.parse({ + provider, + model, + api_key: "test-key", + temperature: 0.4, + }); + const request = prepareModelRequest(config, { context: { messages: [] } }); + + expect(request.model.id).toBe(model); + expect(request.options).not.toHaveProperty("temperature"); + }); + + it("enables mandatory adaptive thinking for Claude Fable 5.1", () => { + const config = AgentConfigSchema.parse({ + provider: "anthropic", + model: "claude-fable-5-1", + api_key: "test-key", + }); + const request = prepareModelRequest(config, { context: { messages: [] } }); + + expect(request.options.thinkingEnabled).toBe(true); + expect(request.model.compat).toMatchObject({ + forceAdaptiveThinking: true, + supportsTemperature: false, + }); + }); + it("passes the configured reasoning effort to Codex", () => { const config = AgentConfigSchema.parse({ provider: "codex", diff --git a/src/agent/model-request.ts b/src/agent/model-request.ts index 3ec1aa7e..1b242146 100644 --- a/src/agent/model-request.ts +++ b/src/agent/model-request.ts @@ -47,6 +47,14 @@ const GOOGLE_MODELS_WITHOUT_SAMPLING_PARAMS = new Set([ function modelSupportsTemperature(provider: SupportedProvider, modelId: string): boolean { if (provider === "codex" || provider === "grok-build") return false; + if (provider === "openai" && modelId === "gpt-6-astra") return false; + if ( + provider === "openrouter" && + ["anthropic/claude-fable-5.1", "openai/gpt-6-astra", "google/gemini-3.8-flash"].includes( + modelId + ) + ) + return false; if (provider === "google" && GOOGLE_MODELS_WITHOUT_SAMPLING_PARAMS.has(modelId)) return false; return true; } @@ -128,6 +136,7 @@ export function prepareModelRequest( signal: request.signal, timeoutMs: request.timeoutMs, ...getReasoningOptions(provider, config.reasoning_effort), + ...(provider === "anthropic" && model.id === "claude-fable-5-1" && { thinkingEnabled: true }), ...getProviderPayloadOptions(provider), } as ProviderStreamOptions, }; diff --git a/src/config/model-catalog.ts b/src/config/model-catalog.ts index 08955897..24c09700 100644 --- a/src/config/model-catalog.ts +++ b/src/config/model-catalog.ts @@ -1,7 +1,7 @@ /** * Shared model catalog used by WebUI setup, CLI onboard, and config routes. * To add a model, add it here — it will appear in all UIs automatically. - * Models must exist in pi-ai's registry (or be entered as custom). + * Models must exist in pi-ai's registry or the additional model definitions. */ export interface ModelOption { @@ -21,6 +21,11 @@ interface CatalogModelOption extends ModelOption { const MODEL_OPTIONS: Record = { anthropic: [ + { + value: "claude-fable-5-1", + name: "Claude Fable 5.1", + description: "Advanced agentic coding, reasoning, vision, 1M context", + }, { value: "claude-fable-5", name: "Claude Fable 5", @@ -68,6 +73,11 @@ const MODEL_OPTIONS: Record = { }, ], openai: [ + { + value: "gpt-6-astra", + name: "GPT-6 Astra", + description: "Advanced reasoning and coding, vision, 272K effective context", + }, { value: "gpt-5.6-sol", name: "GPT-5.6 Sol", @@ -127,6 +137,11 @@ const MODEL_OPTIONS: Record = { name: "GPT-5.6 Terra", description: "Balanced agentic coding model, 272K context", }, + { + value: "gpt-6-astra", + name: "GPT-6 Astra", + description: "Advanced agentic coding, reasoning, vision, 272K context", + }, { value: "gpt-5.6-sol", name: "GPT-5.6 Sol", @@ -210,6 +225,21 @@ const MODEL_OPTIONS: Record = { }, ], openrouter: [ + { + value: "anthropic/claude-fable-5.1", + name: "Claude Fable 5.1", + description: "Advanced Claude reasoning and agentic coding via OpenRouter", + }, + { + value: "openai/gpt-6-astra", + name: "GPT-6 Astra", + description: "Advanced reasoning and coding via OpenRouter", + }, + { + value: "google/gemini-3.8-flash", + name: "Gemini 3.8 Flash", + description: "Agentic coding and multimodal reasoning via OpenRouter", + }, { value: "anthropic/claude-fable-5", name: "Claude Fable 5", diff --git a/src/providers/__tests__/codex-models.test.ts b/src/providers/__tests__/codex-models.test.ts index be974b96..c7a8341f 100644 --- a/src/providers/__tests__/codex-models.test.ts +++ b/src/providers/__tests__/codex-models.test.ts @@ -4,10 +4,10 @@ import { getProviderMetadata } from "../../config/providers.js"; import { AgentConfigSchema } from "../../config/schema.js"; import { getProviderModel } from "../model-resolver.js"; -const GPT_56_CODEX_MODELS = ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"] as const; +const CODEX_MODELS = ["gpt-6-astra", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"] as const; -describe("Codex GPT-5.6 models", () => { - it.each(GPT_56_CODEX_MODELS)("resolves %s through the Codex Responses provider", (modelId) => { +describe("Codex models", () => { + it.each(CODEX_MODELS)("resolves %s through the Codex Responses provider", (modelId) => { const model = getProviderModel("codex", modelId); expect(model.id).toBe(modelId); @@ -18,10 +18,10 @@ describe("Codex GPT-5.6 models", () => { expect(model.maxTokens).toBe(128_000); }); - it("offers every GPT-5.6 model advertised by the Codex backend", () => { + it("offers Astra and every GPT-5.6 model advertised by the Codex backend", () => { const modelIds = getModelsForProvider("codex").map((model) => model.value); - expect(modelIds).toEqual(expect.arrayContaining([...GPT_56_CODEX_MODELS])); + expect(modelIds).toEqual(expect.arrayContaining([...CODEX_MODELS])); expect(AgentConfigSchema.safeParse({ provider: "codex", model: "gpt-5.6-luna" }).success).toBe( true ); diff --git a/src/providers/__tests__/model-catalog.test.ts b/src/providers/__tests__/model-catalog.test.ts index 62ac9ba1..7d1bf3ec 100644 --- a/src/providers/__tests__/model-catalog.test.ts +++ b/src/providers/__tests__/model-catalog.test.ts @@ -92,7 +92,7 @@ describe("provider model catalog", () => { } }); - it("resolves every static catalog entry through pi-ai", () => { + it("resolves every static catalog entry through the model resolver", () => { for (const provider of getSupportedProviders()) { if (PROVIDERS_WITHOUT_PI_REGISTRY_MODELS.has(provider.id)) continue; diff --git a/src/providers/additional-models.ts b/src/providers/additional-models.ts new file mode 100644 index 00000000..a89a381e --- /dev/null +++ b/src/providers/additional-models.ts @@ -0,0 +1,205 @@ +import type { Api, Model } from "@earendil-works/pi-ai/compat"; + +/** + * New models missing from pi-ai 0.82.1. Metadata follows pi-ai 0.85.1 and the + * provider catalogs; keep the existing adapters and catalog until a full upgrade. + * Native registry entries take precedence when pi-ai is updated. + */ +export const ADDITIONAL_MODELS: Record> = { + "anthropic:claude-fable-5-1": { + id: "claude-fable-5-1", + name: "Claude Fable 5.1", + api: "anthropic-messages", + provider: "anthropic", + baseUrl: "https://api.anthropic.com", + reasoning: true, + input: ["text", "image"], + cost: { + input: 10, + output: 50, + cacheRead: 0.25, + cacheWrite: 12.5, + }, + contextWindow: 1000000, + maxTokens: 128000, + compat: { + forceAdaptiveThinking: true, + supportsStrictTools: true, + supportsTemperature: false, + }, + thinkingLevelMap: { + off: null, + xhigh: "xhigh", + max: "max", + }, + }, + "openai:gpt-6-astra": { + id: "gpt-6-astra", + name: "GPT-6 Astra", + api: "openai-responses", + provider: "openai", + baseUrl: "https://api.openai.com/v1", + reasoning: true, + input: ["text", "image"], + cost: { + input: 10, + output: 50, + cacheRead: 1, + cacheWrite: 12.5, + tiers: [ + { + inputTokensAbove: 272000, + input: 20, + output: 75, + cacheRead: 2, + cacheWrite: 25, + }, + ], + }, + contextWindow: 272000, + maxTokens: 128000, + thinkingLevelMap: { + off: null, + minimal: null, + low: "low", + medium: "medium", + high: "high", + xhigh: "xhigh", + max: "max", + }, + compat: { + supportsStrictMode: true, + supportsOpenAIGrammarTools: true, + supportsToolSearch: true, + supportsExplicitPromptCacheMode: true, + }, + }, + "codex:gpt-6-astra": { + id: "gpt-6-astra", + name: "GPT-6 Astra", + api: "openai-codex-responses", + provider: "openai-codex", + baseUrl: "https://chatgpt.com/backend-api", + reasoning: true, + input: ["text", "image"], + cost: { + input: 10, + output: 50, + cacheRead: 1, + cacheWrite: 12.5, + tiers: [ + { + inputTokensAbove: 272000, + input: 20, + output: 75, + cacheRead: 2, + cacheWrite: 25, + }, + ], + }, + contextWindow: 272000, + maxTokens: 128000, + thinkingLevelMap: { + off: null, + minimal: "low", + low: "low", + medium: "medium", + high: "high", + xhigh: "xhigh", + max: "max", + }, + compat: { + supportsOpenAIGrammarTools: true, + supportsToolSearch: true, + }, + }, + "openrouter:anthropic/claude-fable-5.1": { + id: "anthropic/claude-fable-5.1", + name: "Anthropic: Claude Fable 5.1", + api: "openai-completions", + baseUrl: "https://openrouter.ai/api/v1", + provider: "openrouter", + reasoning: true, + thinkingLevelMap: { + off: null, + minimal: null, + low: "low", + medium: "medium", + high: "high", + xhigh: "xhigh", + max: "max", + }, + input: ["text", "image"], + cost: { + input: 10, + output: 50, + cacheRead: 0.25, + cacheWrite: 12.5, + }, + contextWindow: 1000000, + maxTokens: 128000, + compat: { + thinkingFormat: "openrouter", + cacheControlFormat: "anthropic", + }, + }, + "openrouter:openai/gpt-6-astra": { + id: "openai/gpt-6-astra", + name: "OpenAI: GPT-6 Astra", + api: "openai-completions", + baseUrl: "https://openrouter.ai/api/v1", + provider: "openrouter", + reasoning: true, + thinkingLevelMap: { + off: null, + minimal: null, + low: "low", + medium: "medium", + high: "high", + xhigh: "xhigh", + max: "max", + }, + input: ["text", "image"], + cost: { + input: 10, + output: 50, + cacheRead: 1, + cacheWrite: 12.5, + }, + contextWindow: 1050000, + maxTokens: 128000, + compat: { + thinkingFormat: "openrouter", + }, + }, + "openrouter:google/gemini-3.8-flash": { + id: "google/gemini-3.8-flash", + name: "Google: Gemini 3.8 Flash", + api: "openai-completions", + baseUrl: "https://openrouter.ai/api/v1", + provider: "openrouter", + reasoning: true, + thinkingLevelMap: { + off: null, + minimal: null, + low: "low", + medium: "medium", + high: "high", + xhigh: null, + max: null, + }, + input: ["text", "image"], + cost: { + input: 0.75, + output: 3.75, + cacheRead: 0.075, + cacheWrite: 0.041667, + }, + contextWindow: 1048576, + maxTokens: 65536, + compat: { + supportsDeveloperRole: false, + thinkingFormat: "openrouter", + }, + }, +}; diff --git a/src/providers/model-resolver.ts b/src/providers/model-resolver.ts index c8d2f24e..2240ae87 100644 --- a/src/providers/model-resolver.ts +++ b/src/providers/model-resolver.ts @@ -4,6 +4,7 @@ import { createLogger } from "../utils/logger.js"; import { fetchWithTimeout } from "../utils/fetch.js"; import { getGrokBuildCliVersion } from "./grok-build-credentials.js"; import { assertModelAvailable } from "../config/model-catalog.js"; +import { ADDITIONAL_MODELS } from "./additional-models.js"; const log = createLogger("LLM"); @@ -253,7 +254,7 @@ export function getProviderModel(provider: SupportedProvider, modelId: string): try { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- getModel requires literal provider+model types; dynamic strings need casts - const model = getModel(meta.piAiProvider as any, modelId as any); + const model = getModel(meta.piAiProvider as any, modelId as any) ?? ADDITIONAL_MODELS[cacheKey]; if (!model) { throw new Error(`getModel returned undefined for ${provider}/${modelId}`); } From 2ef6b7514e6fbdf3034b880e1e94a5933a3c48bc Mon Sep 17 00:00:00 2001 From: TONresistor <240980241+TONresistor@users.noreply.github.com> Date: Sun, 6 Sep 2026 12:33:56 +0200 Subject: [PATCH 4/4] chore(release): prepare v0.11.2 --- CHANGELOG.md | 18 ++++++++++++++++- package-lock.json | 29 ++++++++++++++------------- package.json | 4 ++-- web/package-lock.json | 46 +++++++++++++++++++++---------------------- 4 files changed, 57 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f97fb33a..6da31361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.11.2] - 2026-09-06 + +### Added + +- Inline URL and copy buttons inside native Telegram Rich Message paragraphs, including a subtle link style. +- Claude Fable 5.1, GPT-6 Astra, and Gemini 3.8 Flash across their supported native, Codex, and OpenRouter providers. + +### Changed + +- Simplified CI and release builds by removing redundant SDK and artifact stages and using the published Docker image as the release cache. + +### Fixed + +- Updated the transitive `fast-uri`, `qs`, and `browserslist` packages to patched versions required by the security audits. + ## [0.11.1] - 2026-08-28 ### Changed @@ -595,7 +610,8 @@ Git history rewritten to fix commit attribution (email update from `tonresistor@ - Professional distribution (npm, Docker, CI/CD) - Pre-commit hooks and linting infrastructure -[Unreleased]: https://github.com/TONresistor/teleton-agent/compare/v0.11.1...HEAD +[Unreleased]: https://github.com/TONresistor/teleton-agent/compare/v0.11.2...HEAD +[0.11.2]: https://github.com/TONresistor/teleton-agent/compare/v0.11.1...v0.11.2 [0.11.1]: https://github.com/TONresistor/teleton-agent/compare/v0.11.0...v0.11.1 [0.11.0]: https://github.com/TONresistor/teleton-agent/compare/v0.10.1...v0.11.0 [0.10.1]: https://github.com/TONresistor/teleton-agent/compare/v0.10.0...v0.10.1 diff --git a/package-lock.json b/package-lock.json index df449f56..6ee50f0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "teleton", - "version": "0.11.1", + "version": "0.11.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "teleton", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "workspaces": [ "packages/*" @@ -7196,9 +7196,9 @@ } }, "node_modules/fast-uri": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", - "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.6.tgz", + "integrity": "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==", "funding": [ { "type": "github", @@ -11022,12 +11022,13 @@ } }, "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -11773,14 +11774,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, diff --git a/package.json b/package.json index 0ef8cd81..645256be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "teleton", - "version": "0.11.1", + "version": "0.11.2", "workspaces": [ "packages/*" ], @@ -101,7 +101,7 @@ "axios": ">=1.18.1", "brace-expansion": "5.0.9", "esbuild": "0.28.1", - "fast-uri": "3.1.5", + "fast-uri": "3.1.6", "ip-address": "10.3.1", "nanoid": "3.3.18", "sharp": "0.35.3" diff --git a/web/package-lock.json b/web/package-lock.json index e9581d36..8aa28bf9 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1312,9 +1312,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.38", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", - "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==", + "version": "2.11.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", + "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1325,9 +1325,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", "dev": true, "funding": [ { @@ -1345,11 +1345,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" }, "bin": { "browserslist": "cli.js" @@ -1359,9 +1359,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001799", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", - "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "dev": true, "funding": [ { @@ -1511,9 +1511,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.376", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz", - "integrity": "sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==", + "version": "1.5.422", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz", + "integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==", "dev": true, "license": "ISC" }, @@ -2694,9 +2694,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.48", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz", - "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==", + "version": "2.0.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", + "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", "dev": true, "license": "MIT", "engines": { @@ -3208,9 +3208,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", "dev": true, "funding": [ {