talon-agent 5.4.1 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +1 -1
- package/prompts/identity.md +2 -0
- package/prompts/telegram.md +2 -1
- package/src/app.ts +8 -0
- package/src/backend/runtime/turn/turn-phases.ts +5 -0
- package/src/bootstrap.ts +55 -24
- package/src/core/agents/runner.ts +50 -2
- package/src/core/agents/types.ts +5 -0
- package/src/core/background/cron/job-oneshot.ts +2 -0
- package/src/core/background/cron/scheduler.ts +45 -2
- package/src/core/background/heartbeat/agent.ts +117 -20
- package/src/core/background/heartbeat/state.ts +11 -0
- package/src/core/config/index.ts +38 -0
- package/src/core/engine/backend-controller/index.ts +1 -0
- package/src/core/engine/backend-controller/pool.ts +11 -0
- package/src/core/engine/backend-router/headroom.ts +267 -0
- package/src/core/engine/backend-router/index.ts +52 -0
- package/src/core/engine/backend-router/ledger.ts +248 -0
- package/src/core/engine/backend-router/router.ts +322 -0
- package/src/core/engine/backend-router/usage.ts +80 -0
- package/src/core/engine/gateway-actions/agents/control.ts +2 -1
- package/src/core/engine/gateway-actions/models.ts +74 -37
- package/src/core/tools/ops/models.ts +2 -2
- package/src/frontend/presentation/plan-usage-report.ts +29 -38
- package/src/frontend/presentation/reports.ts +5 -1
- package/src/frontend/telegram/formatting.ts +39 -0
- package/src/frontend/terminal/builtins/status.ts +29 -1
- package/src/util/log.ts +1 -0
|
@@ -89,6 +89,36 @@ function applyInlineFormatting(input: string, urls: string[]): string {
|
|
|
89
89
|
return out;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Block-level passes over already-escaped text: ATX headings → bold lines,
|
|
94
|
+
* runs of `> ` lines → one `<blockquote>`. Placeholders for code are opaque
|
|
95
|
+
* `\x00…\x00` tokens, so a `#` inside a fenced block is never seen here.
|
|
96
|
+
*/
|
|
97
|
+
function applyBlockFormatting(input: string): string {
|
|
98
|
+
const lines = input.split("\n");
|
|
99
|
+
const out: string[] = [];
|
|
100
|
+
let quote: string[] | null = null;
|
|
101
|
+
const flushQuote = () => {
|
|
102
|
+
if (quote) out.push(`<blockquote>${quote.join("\n")}</blockquote>`);
|
|
103
|
+
quote = null;
|
|
104
|
+
};
|
|
105
|
+
for (const line of lines) {
|
|
106
|
+
const quoted = /^>[ \t]?(.*)$/.exec(line);
|
|
107
|
+
if (quoted) {
|
|
108
|
+
(quote ??= []).push(quoted[1] ?? "");
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
flushQuote();
|
|
112
|
+
// `#` must be followed by whitespace to be a heading: `#993` is a
|
|
113
|
+
// reference, not a title. Trailing closing hashes (`## Title ##`) are
|
|
114
|
+
// markdown too and are dropped.
|
|
115
|
+
const heading = /^#{1,6}[ \t]+(.+?)(?:[ \t]+#+)?[ \t]*$/.exec(line);
|
|
116
|
+
out.push(heading ? `<b>${heading[1]}</b>` : line);
|
|
117
|
+
}
|
|
118
|
+
flushQuote();
|
|
119
|
+
return out.join("\n");
|
|
120
|
+
}
|
|
121
|
+
|
|
92
122
|
/**
|
|
93
123
|
* Convert Markdown output to Telegram-safe HTML.
|
|
94
124
|
*
|
|
@@ -145,6 +175,15 @@ export function markdownToTelegramHtml(text: string): string {
|
|
|
145
175
|
// oxlint-disable-next-line no-control-regex
|
|
146
176
|
processed = processed.replace(/[^`\x00]+/g, (segment) => escapeHtml(segment));
|
|
147
177
|
|
|
178
|
+
// Step 3b: Block-level markdown. Telegram HTML has no headings, so an
|
|
179
|
+
// ATX heading becomes a bold line — the alternative is the `###` going
|
|
180
|
+
// out raw, and `# Title` alone turns `#Title` into a hashtag entity.
|
|
181
|
+
// Only `#` followed by whitespace is a heading: `#993` is a PR/hashtag
|
|
182
|
+
// reference and stays literal. Runs of `> ` lines become a blockquote,
|
|
183
|
+
// which Telegram does render. The text is already escaped here, so the
|
|
184
|
+
// quote marker is `>`.
|
|
185
|
+
processed = applyBlockFormatting(processed);
|
|
186
|
+
|
|
148
187
|
// Step 4: Apply inline formatting, but only keep it if the result is
|
|
149
188
|
// actually parseable. `processed` at this point holds escaped text plus
|
|
150
189
|
// tag-free placeholders, so it is a safe unformatted fallback.
|
|
@@ -14,7 +14,14 @@ import {
|
|
|
14
14
|
import { getChatSettings } from "../../../storage/chat-settings.js";
|
|
15
15
|
import { getSessionInfo } from "../../../storage/sessions.js";
|
|
16
16
|
import { getLoadedPlugins } from "../../../core/plugin/index.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
getPoolConfig,
|
|
19
|
+
getPooledBackend,
|
|
20
|
+
} from "../../../core/engine/backend-controller/index.js";
|
|
21
|
+
import {
|
|
22
|
+
collectBackendUsage,
|
|
23
|
+
formatHeadroom,
|
|
24
|
+
} from "../../../core/engine/backend-router/index.js";
|
|
18
25
|
import type { Command, CommandContext } from "../command-registry.js";
|
|
19
26
|
|
|
20
27
|
type BackendRef = CommandContext["backend"];
|
|
@@ -146,6 +153,27 @@ async function writePlan(ctx: CommandContext, be: BackendRef): Promise<void> {
|
|
|
146
153
|
);
|
|
147
154
|
}
|
|
148
155
|
}
|
|
156
|
+
await writeHeadroom(ctx);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* What the plan-aware router sees: one comparable figure per backend, so
|
|
161
|
+
* "why did that sub-agent run on agy?" is answerable from `/status`.
|
|
162
|
+
* Ledger-derived rows say so — they are Talon's own count, not the
|
|
163
|
+
* provider's.
|
|
164
|
+
*/
|
|
165
|
+
async function writeHeadroom(ctx: CommandContext): Promise<void> {
|
|
166
|
+
const entries = await collectBackendUsage(getPoolConfig() ?? undefined).catch(
|
|
167
|
+
() => [],
|
|
168
|
+
);
|
|
169
|
+
if (entries.length === 0) return;
|
|
170
|
+
ctx.renderer.writeln();
|
|
171
|
+
ctx.renderer.writeln(` ${pc.bold("Headroom")}`);
|
|
172
|
+
for (const entry of entries) {
|
|
173
|
+
ctx.renderer.writeln(
|
|
174
|
+
` ${(entry.label || entry.id).padEnd(14)}${pc.dim(formatHeadroom(entry.headroom))}`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
149
177
|
}
|
|
150
178
|
|
|
151
179
|
function writePlugins(ctx: CommandContext): void {
|