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": [
{