talon-agent 5.0.0 → 5.1.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/package.json +2 -2
- package/src/app.ts +3 -3
- package/src/backend/claude-sdk/factory.ts +21 -41
- package/src/backend/claude-sdk/handler.ts +191 -69
- package/src/backend/claude-sdk/host/in-process.ts +147 -0
- package/src/backend/claude-sdk/one-shot.ts +17 -2
- package/src/backend/claude-sdk/stream.ts +9 -0
- package/src/backend/codex/mcp-config.ts +1 -1
- package/src/backend/codex/one-shot.ts +18 -4
- package/src/backend/openai-agents/mcp-pool.ts +1 -1
- package/src/backend/remote-server/one-shot.ts +16 -3
- package/src/backend/runtime/index.ts +2 -2
- package/src/backend/runtime/one-shot-hooks.ts +45 -0
- package/src/backend/runtime/turn/delivery.ts +1 -1
- package/src/backend/runtime/turn/turn-phases.ts +1 -1
- package/src/bootstrap.ts +2 -2
- package/src/cli.ts +5 -2
- package/src/core/agent-runtime/README.md +15 -0
- package/src/core/agent-runtime/agent-host.ts +388 -0
- package/src/core/agent-runtime/capabilities.ts +3 -0
- package/src/core/background/cron/scheduler.ts +1 -1
- package/src/core/background/triggers/command.ts +1 -1
- package/src/{util → core/config}/harden.ts +1 -1
- package/src/core/config/index.ts +1 -1
- package/src/core/daemon/pidfile.ts +1 -1
- package/src/core/daemon/resource-sampler.ts +2 -2
- package/src/{util → core/daemon}/respawn.ts +1 -1
- package/src/core/doctor/index.ts +1 -1
- package/src/core/engine/gateway-actions/fetch-url.ts +1 -1
- package/src/core/engine/gateway-actions/index.ts +1 -1
- package/src/core/engine/gateway-actions/memory.ts +2 -2
- package/src/core/engine/gateway-actions/native/exec-background.ts +129 -0
- package/src/core/engine/gateway-actions/native/exec-remote.ts +85 -0
- package/src/core/engine/gateway-actions/native/exec.ts +182 -0
- package/src/core/engine/gateway-actions/native/index.ts +46 -0
- package/src/core/engine/gateway-actions/native/params.ts +46 -0
- package/src/core/engine/gateway-actions/native/read.ts +182 -0
- package/src/core/engine/gateway-actions/native/results.ts +34 -0
- package/src/core/engine/gateway-actions/native/search.ts +212 -0
- package/src/core/engine/gateway-actions/native/shell.ts +39 -0
- package/src/core/engine/gateway-actions/native/teleport.ts +55 -0
- package/src/core/engine/gateway-actions/native/write.ts +170 -0
- package/src/core/frontend-runtime/builtins.ts +2 -2
- package/src/core/mcp-hub/children.ts +21 -5
- package/src/core/mcp-hub/index.ts +1 -1
- package/src/core/mcp-hub/talon-server.ts +1 -1
- package/src/core/memory/taps.ts +1 -1
- package/src/core/plugin/mcp.ts +1 -1
- package/src/core/scripts/lua.ts +1 -1
- package/src/core/tools/{chat.ts → chat/chat.ts} +1 -1
- package/src/core/tools/{cross-send.ts → chat/cross-send.ts} +1 -1
- package/src/core/tools/{history.ts → chat/history.ts} +2 -2
- package/src/core/tools/{media.ts → chat/media.ts} +1 -1
- package/src/core/tools/{members.ts → chat/members.ts} +2 -2
- package/src/core/tools/{messaging.ts → chat/messaging.ts} +2 -2
- package/src/core/tools/{moderation.ts → chat/moderation.ts} +2 -2
- package/src/core/tools/{stickers.ts → chat/stickers.ts} +2 -2
- package/src/core/tools/{whatsapp.ts → chat/whatsapp.ts} +1 -1
- package/src/core/tools/{memory.ts → content/memory.ts} +2 -2
- package/src/core/tools/{web.ts → content/web.ts} +1 -1
- package/src/core/tools/index.ts +20 -20
- package/src/core/tools/{admin.ts → ops/admin.ts} +1 -1
- package/src/core/tools/{bridge.ts → ops/bridge.ts} +2 -2
- package/src/core/tools/{goals.ts → ops/goals.ts} +2 -2
- package/src/core/tools/{mesh.ts → ops/mesh.ts} +1 -1
- package/src/core/tools/{models.ts → ops/models.ts} +1 -1
- package/src/core/tools/{native.ts → ops/native.ts} +1 -1
- package/src/core/tools/{scheduling.ts → ops/scheduling.ts} +1 -1
- package/src/core/tools/{scripts.ts → ops/scripts.ts} +1 -1
- package/src/core/tools/{skills.ts → ops/skills.ts} +1 -1
- package/src/core/tools/{triggers.ts → ops/triggers.ts} +1 -1
- package/src/core/types.ts +11 -0
- package/src/{util → core/vfs}/workspace.ts +2 -2
- package/src/frontend/discord/callbacks/components/effort.ts +1 -1
- package/src/frontend/discord/callbacks/components/index.ts +1 -1
- package/src/frontend/discord/callbacks/components/settings.ts +1 -1
- package/src/frontend/discord/commands/admin.ts +1 -1
- package/src/frontend/discord/commands/info.ts +1 -1
- package/src/frontend/discord/commands/interaction.ts +1 -1
- package/src/frontend/discord/commands/session.ts +3 -3
- package/src/frontend/discord/commands/settings.ts +1 -1
- package/src/frontend/discord/handlers/messages.ts +1 -1
- package/src/frontend/discord/handlers/state.ts +1 -1
- package/src/frontend/discord/middleware.ts +1 -1
- package/src/frontend/discord/ready.ts +1 -1
- package/src/frontend/discord/render.ts +41 -259
- package/src/frontend/native/chats/chats.ts +1 -1
- package/src/frontend/native/surface/models.ts +1 -1
- package/src/frontend/native/turn/context.ts +1 -1
- package/src/frontend/native/turn/emit.ts +1 -1
- package/src/frontend/{shared → presentation}/format.ts +4 -3
- package/src/frontend/{shared → presentation}/reasoning-levels.ts +12 -0
- package/src/frontend/presentation/reports.ts +493 -0
- package/src/frontend/{shared → presentation}/session-status.ts +1 -1
- package/src/frontend/teams/commands.ts +2 -2
- package/src/frontend/teams/turn.ts +1 -1
- package/src/frontend/telegram/admin/health.ts +1 -1
- package/src/frontend/telegram/admin/sessions.ts +1 -1
- package/src/frontend/telegram/callbacks/effort.ts +2 -2
- package/src/frontend/telegram/callbacks/metrics.ts +1 -1
- package/src/frontend/telegram/callbacks/model/views.ts +1 -1
- package/src/frontend/telegram/callbacks/query.ts +1 -1
- package/src/frontend/telegram/callbacks/settings.ts +2 -2
- package/src/frontend/telegram/commands/admin.ts +4 -4
- package/src/frontend/telegram/commands/info.ts +2 -2
- package/src/frontend/telegram/commands/session.ts +4 -4
- package/src/frontend/telegram/commands/settings.ts +4 -2
- package/src/frontend/telegram/handlers/state.ts +1 -1
- package/src/frontend/telegram/model-menu.ts +1 -1
- package/src/frontend/telegram/render/html.ts +30 -0
- package/src/frontend/telegram/{helpers → render}/menu.ts +46 -19
- package/src/frontend/telegram/render/reports.ts +182 -0
- package/src/frontend/terminal/builtins/context.ts +2 -2
- package/src/frontend/terminal/builtins/session.ts +1 -1
- package/src/frontend/terminal/builtins/status.ts +2 -2
- package/src/frontend/terminal/index.ts +2 -2
- package/src/frontend/terminal/renderer.ts +1 -1
- package/src/frontend/whatsapp/commands.ts +5 -5
- package/src/frontend/whatsapp/registry.ts +1 -1
- package/src/index.ts +5 -2
- package/src/util/runtime.ts +1 -1
- package/src/core/engine/gateway-actions/native.ts +0 -1035
- package/src/frontend/telegram/helpers/diagnostics.ts +0 -428
- package/src/frontend/telegram/helpers/format.ts +0 -42
- package/src/frontend/telegram/helpers/index.ts +0 -13
- package/src/util/cleanup-registry.ts +0 -36
- /package/src/{util → core/daemon}/boot-timer.ts +0 -0
- /package/src/{util → core/frontend-runtime}/chat-id.ts +0 -0
- /package/src/{util/mcp-launcher.ts → core/mcp-hub/launcher.ts} +0 -0
- /package/src/{util → core/tools/content}/web-content.ts +0 -0
- /package/src/core/tools/{mcp-env.ts → ops/mcp-env.ts} +0 -0
- /package/src/{util → core/weaver}/session-name.ts +0 -0
- /package/src/frontend/{shared → presentation}/access.ts +0 -0
- /package/src/frontend/{shared → presentation}/model-commands.ts +0 -0
- /package/src/frontend/{shared → presentation}/plan-usage-report.ts +0 -0
- /package/src/frontend/{shared → presentation}/status-context.ts +0 -0
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
* Message rendering for Discord commands, callbacks, and the settings panel:
|
|
3
3
|
* metrics, doctor, mesh, usage and settings text, sized to Discord's limits.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* The reports themselves live in `frontend/presentation/reports.ts` — this
|
|
6
|
+
* file is Discord's dialect of them (`DISCORD_REPORTS`) plus the wrappers
|
|
7
|
+
* that keep each command to a single import.
|
|
8
|
+
*
|
|
9
|
+
* Discord-specific quirks vs the Telegram renderers:
|
|
6
10
|
* - settings panel uses Components (Buttons + Select Menus), not inline keyboard.
|
|
7
11
|
* - custom_id strings are limited to 100 chars total — keep payload compact.
|
|
8
12
|
* - chat IDs are Discord snowflakes (strings), not numbers.
|
|
13
|
+
* - markdown needs no escaping, so `escape` is the identity function.
|
|
9
14
|
*/
|
|
10
15
|
|
|
11
|
-
import { REASONING_LEVEL_DESCRIPTIONS } from "../../core/models/reasoning-levels.js";
|
|
12
16
|
import {
|
|
13
17
|
DISCORD_MAX_TEXT,
|
|
14
18
|
DISCORD_SAFE_RESERVE,
|
|
@@ -16,13 +20,18 @@ import {
|
|
|
16
20
|
} from "./formatting.js";
|
|
17
21
|
import type { DoctorReport } from "../../core/doctor/index.js";
|
|
18
22
|
import type { MeshPingResult } from "../../core/mesh/service.js";
|
|
19
|
-
import type { BackendUsageEntry } from "../
|
|
23
|
+
import type { BackendUsageEntry } from "../presentation/plan-usage-report.js";
|
|
20
24
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
renderDoctorReport,
|
|
26
|
+
renderMeshReport as renderMeshReportWith,
|
|
27
|
+
renderMetricsMessages as renderMetricsMessagesWith,
|
|
28
|
+
renderSettingsText as renderSettingsTextWith,
|
|
29
|
+
renderUsageMessage as renderUsageMessageWith,
|
|
30
|
+
type MetricsSnapshot,
|
|
31
|
+
type ReportFormatter,
|
|
32
|
+
} from "../presentation/reports.js";
|
|
33
|
+
|
|
34
|
+
export { EFFORT_DESCRIPTIONS } from "../presentation/reasoning-levels.js";
|
|
26
35
|
|
|
27
36
|
export {
|
|
28
37
|
parseInterval,
|
|
@@ -31,28 +40,23 @@ export {
|
|
|
31
40
|
formatBytes,
|
|
32
41
|
formatUsd,
|
|
33
42
|
formatModelLabel,
|
|
34
|
-
} from "../
|
|
43
|
+
} from "../presentation/format.js";
|
|
35
44
|
|
|
36
|
-
/** Per-message length budget for
|
|
45
|
+
/** Per-message length budget for report output. */
|
|
37
46
|
const DEFAULT_METRICS_MESSAGE_MAX = DISCORD_MAX_TEXT - DISCORD_SAFE_RESERVE;
|
|
38
47
|
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
>;
|
|
48
|
+
/** Discord markdown: bold/italic/code markers, no escaping, 2000-char messages. */
|
|
49
|
+
const DISCORD_REPORTS: ReportFormatter = {
|
|
50
|
+
bold: (s) => `**${s}**`,
|
|
51
|
+
italic: (s) => `_${s}_`,
|
|
52
|
+
emphasis: (s) => `*${s}*`,
|
|
53
|
+
code: (s) => `\`${s}\``,
|
|
54
|
+
escape: (s) => s,
|
|
55
|
+
lineLimit: DEFAULT_METRICS_MESSAGE_MAX,
|
|
56
|
+
metricLabelMax: 60,
|
|
57
|
+
pulseLabel: "🔔 Pulse:",
|
|
50
58
|
};
|
|
51
59
|
|
|
52
|
-
function truncateMetricLabel(label: string, max = 60): string {
|
|
53
|
-
return label.length <= max ? label : `${label.slice(0, max - 3)}...`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
60
|
/**
|
|
57
61
|
* Render the metrics report into one or more Discord messages, each ≤ maxLen
|
|
58
62
|
* (default ~1900 chars to leave headroom under the 2000-char limit).
|
|
@@ -62,117 +66,9 @@ export function renderMetricsMessages(
|
|
|
62
66
|
maxLen = DEFAULT_METRICS_MESSAGE_MAX,
|
|
63
67
|
title = "📊 Metrics",
|
|
64
68
|
): string[] {
|
|
65
|
-
|
|
66
|
-
const continuationHeader = `**${title} (cont.)**`;
|
|
67
|
-
const sections: string[][] = [];
|
|
68
|
-
|
|
69
|
-
// Histograms come in two flavours: durations (keys ending in `_ms`,
|
|
70
|
-
// rendered as human times) and plain counts like `tool_calls_per_turn`
|
|
71
|
-
// (rendered as bare numbers — "min=1ms" for a count is nonsense).
|
|
72
|
-
const histKeys = Object.keys(metrics.histograms).sort();
|
|
73
|
-
const durationKeys = histKeys.filter((key) => key.endsWith("_ms"));
|
|
74
|
-
const countKeys = histKeys.filter((key) => !key.endsWith("_ms"));
|
|
75
|
-
const histLine = (key: string, fmt: (v: number) => string): string => {
|
|
76
|
-
const h = metrics.histograms[key];
|
|
77
|
-
return (
|
|
78
|
-
` \`${truncateMetricLabel(key)}\` n=${h.count} ` +
|
|
79
|
-
`avg=${fmt(h.avg)} min=${fmt(h.min)} ` +
|
|
80
|
-
`max=${fmt(h.max)}`
|
|
81
|
-
);
|
|
82
|
-
};
|
|
83
|
-
if (durationKeys.length > 0) {
|
|
84
|
-
sections.push([
|
|
85
|
-
"**Latency**",
|
|
86
|
-
...durationKeys.map((key) => histLine(key, formatDuration)),
|
|
87
|
-
]);
|
|
88
|
-
}
|
|
89
|
-
if (countKeys.length > 0) {
|
|
90
|
-
sections.push([
|
|
91
|
-
"**Distributions**",
|
|
92
|
-
...countKeys.map((key) => histLine(key, String)),
|
|
93
|
-
]);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const counterKeys = Object.keys(metrics.counters).sort();
|
|
97
|
-
if (counterKeys.length > 0) {
|
|
98
|
-
const groups = new Map<string, string[]>();
|
|
99
|
-
for (const key of counterKeys) {
|
|
100
|
-
const prefix = key.includes(".") ? key.split(".")[0]! : "general";
|
|
101
|
-
if (!groups.has(prefix)) groups.set(prefix, []);
|
|
102
|
-
groups.get(prefix)!.push(key);
|
|
103
|
-
}
|
|
104
|
-
for (const prefix of [...groups.keys()].sort()) {
|
|
105
|
-
// tool_calls reads best as a leaderboard — busiest tools first.
|
|
106
|
-
// Other groups keep alphabetical order (stable lookup by name).
|
|
107
|
-
const keys =
|
|
108
|
-
prefix === "tool_calls"
|
|
109
|
-
? [...groups.get(prefix)!].sort(
|
|
110
|
-
(a, b) =>
|
|
111
|
-
metrics.counters[b]! - metrics.counters[a]! ||
|
|
112
|
-
a.localeCompare(b),
|
|
113
|
-
)
|
|
114
|
-
: groups.get(prefix)!;
|
|
115
|
-
sections.push([
|
|
116
|
-
`**${prefix}**`,
|
|
117
|
-
...keys.map((key) => {
|
|
118
|
-
const label = key.includes(".")
|
|
119
|
-
? key.split(".").slice(1).join(".")
|
|
120
|
-
: key;
|
|
121
|
-
return ` \`${truncateMetricLabel(label)}\` ${metrics.counters[key]!.toLocaleString()}`;
|
|
122
|
-
}),
|
|
123
|
-
]);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (sections.length === 0) {
|
|
128
|
-
return [`${firstHeader}\n\n_No metrics recorded yet._`];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const chunks: string[] = [];
|
|
132
|
-
let header = firstHeader;
|
|
133
|
-
let current = header;
|
|
134
|
-
const flush = () => {
|
|
135
|
-
chunks.push(current);
|
|
136
|
-
header = continuationHeader;
|
|
137
|
-
current = header;
|
|
138
|
-
};
|
|
139
|
-
const appendLine = (line: string) => {
|
|
140
|
-
if (!line && current === header) return;
|
|
141
|
-
const candidate = `${current}\n${line}`;
|
|
142
|
-
if (candidate.length <= maxLen) {
|
|
143
|
-
current = candidate;
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
if (current !== header) {
|
|
147
|
-
flush();
|
|
148
|
-
if (!line) return;
|
|
149
|
-
}
|
|
150
|
-
const available = maxLen - header.length - 1;
|
|
151
|
-
if (available < 0) return;
|
|
152
|
-
const safeLine =
|
|
153
|
-
line.length <= available
|
|
154
|
-
? line
|
|
155
|
-
: available >= 4
|
|
156
|
-
? `${line.slice(0, available - 3)}...`
|
|
157
|
-
: line.slice(0, available);
|
|
158
|
-
current = `${current}\n${safeLine}`;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
for (const section of sections) {
|
|
162
|
-
appendLine("");
|
|
163
|
-
for (const line of section) appendLine(line);
|
|
164
|
-
}
|
|
165
|
-
if (current !== header || chunks.length === 0) chunks.push(current);
|
|
166
|
-
return chunks;
|
|
69
|
+
return renderMetricsMessagesWith(metrics, DISCORD_REPORTS, maxLen, title);
|
|
167
70
|
}
|
|
168
71
|
|
|
169
|
-
const DOCTOR_ICONS: Record<string, string> = {
|
|
170
|
-
ok: "✅",
|
|
171
|
-
warn: "⚠️",
|
|
172
|
-
fail: "❌",
|
|
173
|
-
info: "▫️",
|
|
174
|
-
};
|
|
175
|
-
|
|
176
72
|
/**
|
|
177
73
|
* Render a DoctorReport as Discord markdown, split to fit the message cap.
|
|
178
74
|
* Same data as `talon doctor` and Telegram's /doctor.
|
|
@@ -181,59 +77,7 @@ export function renderDoctorMessages(
|
|
|
181
77
|
report: DoctorReport,
|
|
182
78
|
maxLen = DEFAULT_METRICS_MESSAGE_MAX,
|
|
183
79
|
): string[] {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const render = (check: DoctorReport["checks"][number]): string =>
|
|
187
|
-
`${DOCTOR_ICONS[check.status]} ${check.label}${check.detail ? ` (${check.detail})` : ""}`;
|
|
188
|
-
|
|
189
|
-
for (const check of report.checks.filter((c) => !c.inactive)) {
|
|
190
|
-
lines.push(render(check));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Configured-but-idle backends get their own block: they describe what a
|
|
194
|
-
// switch would run into, not the state of the running deployment.
|
|
195
|
-
const idle = report.checks.filter((c) => c.inactive);
|
|
196
|
-
if (idle.length > 0) {
|
|
197
|
-
lines.push("", "**Other backends**", ...idle.map(render));
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
lines.push("", "**Native modules**");
|
|
201
|
-
for (const mod of report.native) {
|
|
202
|
-
const size =
|
|
203
|
-
mod.sizeBytes !== undefined ? ` · ${formatBytes(mod.sizeBytes)}` : "";
|
|
204
|
-
const note = mod.note ? ` (${mod.note})` : "";
|
|
205
|
-
lines.push(
|
|
206
|
-
`${mod.ok ? DOCTOR_ICONS.ok : DOCTOR_ICONS.fail} \`${mod.name}\` — ${mod.language} → ${mod.target}${size}${note}`,
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
lines.push(
|
|
211
|
-
"",
|
|
212
|
-
"**Process**",
|
|
213
|
-
`Uptime ${formatDuration(process.uptime() * 1000)} · PID ${process.pid} · Node ${process.versions.node}`,
|
|
214
|
-
"",
|
|
215
|
-
report.issues === 0
|
|
216
|
-
? `${DOCTOR_ICONS.ok} All checks passed.`
|
|
217
|
-
: `${DOCTOR_ICONS.warn} ${report.issues} issue(s) found.`,
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
return splitMessage(lines.join("\n"), maxLen);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function meshDeviceLine(r: MeshPingResult, now: number): string {
|
|
224
|
-
const d = r.device;
|
|
225
|
-
const bits: string[] = [d.platform];
|
|
226
|
-
if (r.reachable && typeof r.latencyMs === "number") {
|
|
227
|
-
bits.push(`${r.latencyMs} ms`);
|
|
228
|
-
} else if (d.online && r.error) {
|
|
229
|
-
bits.push(r.error);
|
|
230
|
-
} else if (!d.online) {
|
|
231
|
-
bits.push(`last seen ${formatDuration(now - d.lastSeen)} ago`);
|
|
232
|
-
}
|
|
233
|
-
if (typeof d.battery === "number") {
|
|
234
|
-
bits.push(`${d.battery}%${d.charging ? " charging" : ""}`);
|
|
235
|
-
}
|
|
236
|
-
return ` **${d.name}** — ${bits.join(" · ")}`;
|
|
80
|
+
return splitMessage(renderDoctorReport(report, DISCORD_REPORTS), maxLen);
|
|
237
81
|
}
|
|
238
82
|
|
|
239
83
|
/**
|
|
@@ -244,71 +88,12 @@ export function renderMeshReport(
|
|
|
244
88
|
results: MeshPingResult[],
|
|
245
89
|
now = Date.now(),
|
|
246
90
|
): string {
|
|
247
|
-
|
|
248
|
-
return "**Mesh**\n\n_No devices have registered yet._";
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const responding = results
|
|
252
|
-
.filter((r) => r.reachable)
|
|
253
|
-
.sort((a, b) => (a.latencyMs ?? Infinity) - (b.latencyMs ?? Infinity));
|
|
254
|
-
const unreachable = results
|
|
255
|
-
.filter((r) => !r.reachable && r.device.online)
|
|
256
|
-
.sort((a, b) => a.device.name.localeCompare(b.device.name));
|
|
257
|
-
const offline = results
|
|
258
|
-
.filter((r) => !r.reachable && !r.device.online)
|
|
259
|
-
.sort((a, b) => b.device.lastSeen - a.device.lastSeen);
|
|
260
|
-
|
|
261
|
-
const summary = [
|
|
262
|
-
`${results.length} device${results.length === 1 ? "" : "s"}`,
|
|
263
|
-
`${responding.length} responding`,
|
|
264
|
-
...(unreachable.length > 0 ? [`${unreachable.length} unreachable`] : []),
|
|
265
|
-
...(offline.length > 0 ? [`${offline.length} offline`] : []),
|
|
266
|
-
].join(" · ");
|
|
267
|
-
|
|
268
|
-
const lines = ["**Mesh**", summary];
|
|
269
|
-
const section = (title: string, entries: MeshPingResult[]): void => {
|
|
270
|
-
if (entries.length === 0) return;
|
|
271
|
-
lines.push(
|
|
272
|
-
"",
|
|
273
|
-
`**${title}**`,
|
|
274
|
-
...entries.map((r) => meshDeviceLine(r, now)),
|
|
275
|
-
);
|
|
276
|
-
};
|
|
277
|
-
section("Responding", responding);
|
|
278
|
-
section("Unreachable", unreachable);
|
|
279
|
-
section("Offline", offline);
|
|
280
|
-
|
|
281
|
-
return lines.join("\n");
|
|
91
|
+
return renderMeshReportWith(results, DISCORD_REPORTS, now);
|
|
282
92
|
}
|
|
283
93
|
|
|
284
94
|
/** Render the `/usage` report — one block per exposed backend. */
|
|
285
95
|
export function renderUsageMessage(entries: BackendUsageEntry[]): string {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
for (const entry of entries) {
|
|
289
|
-
const name = entry.label || entry.id;
|
|
290
|
-
if (!entry.plan) {
|
|
291
|
-
lines.push("", `**${name}** — _${entry.note ?? ""}_`);
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
const age = entry.plan.ageLabel ? ` *(${entry.plan.ageLabel})*` : "";
|
|
295
|
-
const plan = entry.plan.plan ? ` · ${entry.plan.plan}` : "";
|
|
296
|
-
lines.push("", `**${name}**${plan}${age}`);
|
|
297
|
-
if (entry.plan.resetsAvailable) {
|
|
298
|
-
const n = entry.plan.resetsAvailable;
|
|
299
|
-
lines.push(
|
|
300
|
-
` • You have **${n}** usage limit reset${n === 1 ? "" : "s"} available`,
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
for (const w of entry.plan.windows) {
|
|
304
|
-
const reset = w.resetLabel ? ` reset ${w.resetLabel}` : "";
|
|
305
|
-
lines.push(
|
|
306
|
-
` \`${w.label.padEnd(6)}${w.bar} ${String(w.percent).padStart(3)}%\`${reset}`,
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return lines.join("\n");
|
|
96
|
+
return renderUsageMessageWith(entries, DISCORD_REPORTS);
|
|
312
97
|
}
|
|
313
98
|
|
|
314
99
|
/** Settings panel: build the markdown body. */
|
|
@@ -319,15 +104,12 @@ export function renderSettingsText(
|
|
|
319
104
|
pulseIntervalMs?: number,
|
|
320
105
|
modelDetails?: Array<string>,
|
|
321
106
|
): string {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
`**Effort:** ${effort}`,
|
|
331
|
-
`**🔔 Pulse:** ${proactive ? "on" : "off"} (every ${intervalStr})`,
|
|
332
|
-
].join("\n");
|
|
107
|
+
return renderSettingsTextWith(
|
|
108
|
+
DISCORD_REPORTS,
|
|
109
|
+
model,
|
|
110
|
+
effort,
|
|
111
|
+
proactive,
|
|
112
|
+
pulseIntervalMs,
|
|
113
|
+
modelDetails,
|
|
114
|
+
);
|
|
333
115
|
}
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
listAvailableBackends,
|
|
22
22
|
rebindChat,
|
|
23
23
|
} from "../../../core/engine/backend-controller/index.js";
|
|
24
|
-
import { getActiveReasoningLevels } from "../../
|
|
24
|
+
import { getActiveReasoningLevels } from "../../presentation/reasoning-levels.js";
|
|
25
25
|
import { broadcastChatUpdated } from "../chats/chat-wire.js";
|
|
26
26
|
import { emitSystem } from "../turn/emit.js";
|
|
27
27
|
import type { BackendOption, ModelOption } from "../protocol.js";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { getSessionInfo } from "../../../storage/sessions.js";
|
|
7
|
-
import { buildContextDisplay } from "../../
|
|
7
|
+
import { buildContextDisplay } from "../../presentation/status-context.js";
|
|
8
8
|
import { resolveActiveModelForChat } from "../../../core/models/active-model.js";
|
|
9
9
|
import {
|
|
10
10
|
getBackendForChat,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { pushMessage } from "../../../storage/history.js";
|
|
7
|
-
import { extractSessionName } from "../../../
|
|
7
|
+
import { extractSessionName } from "../../../core/weaver/session-name.js";
|
|
8
8
|
import { broadcastChatUpdated } from "../chats/chat-wire.js";
|
|
9
9
|
import { DEFAULT_CHAT_TITLE, type ChatEntry } from "../chats/chats.js";
|
|
10
10
|
import { mediaUrl, registerMedia } from "../media/media.js";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Frontend-agnostic display formatters shared by all chat frontends.
|
|
3
3
|
*
|
|
4
|
-
* These were previously duplicated per-frontend (
|
|
5
|
-
* discord/helpers.ts) and had started to drift —
|
|
6
|
-
* on Discord but not Telegram. One definition
|
|
4
|
+
* These were previously duplicated per-frontend (the old
|
|
5
|
+
* telegram/helpers/format.ts, discord/helpers.ts) and had started to drift —
|
|
6
|
+
* parseInterval accepted days on Discord but not Telegram. One definition
|
|
7
|
+
* here keeps them in lockstep; frontends import this module directly.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import { resolveModel } from "../../core/models/catalog.js";
|
|
@@ -8,9 +8,21 @@ import type { TalonConfig } from "../../core/config/index.js";
|
|
|
8
8
|
import {
|
|
9
9
|
normalizeReasoningLevels,
|
|
10
10
|
supportsReasoningLevel,
|
|
11
|
+
REASONING_LEVEL_DESCRIPTIONS,
|
|
11
12
|
} from "../../core/models/reasoning-levels.js";
|
|
12
13
|
export { supportsReasoningLevel };
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Effort-level descriptions shown next to each option in a picker.
|
|
17
|
+
*
|
|
18
|
+
* One home for both frontends: Discord renders them as select-menu
|
|
19
|
+
* descriptions, and anything else offering the same choice reads the
|
|
20
|
+
* same table rather than copying it.
|
|
21
|
+
*/
|
|
22
|
+
export const EFFORT_DESCRIPTIONS: Record<string, string> = {
|
|
23
|
+
...REASONING_LEVEL_DESCRIPTIONS,
|
|
24
|
+
};
|
|
25
|
+
|
|
14
26
|
export type ActiveReasoningLevels = {
|
|
15
27
|
activeModel: string | null;
|
|
16
28
|
modelInfo?: UnifiedModelInfo;
|