@llblab/pi-telegram 0.25.0 → 0.25.1
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/AGENTS.md +1 -1
- package/BACKLOG.md +3 -10
- package/CHANGELOG.md +5 -0
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +2 -2
- package/lib/activity-verbosity.ts +65 -5
- package/lib/config.ts +22 -16
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -121,7 +121,7 @@ The core product loop is mobile continuation: start or supervise work in the ter
|
|
|
121
121
|
- Forward-annotation grouping distinguishes two semantic layers: the forward owns its source text/caption/media, while an optional separate owner-authored annotation normally arrives immediately before it. Use a bounded one-second candidate window in both transport orders: ordinary short human text may wait for an adjacent same-sender/chat/thread forward, and a forwarded message—including a media-only forward with no source caption—may wait for one adjacent owner annotation. A matching opposite-kind message flushes immediately; same-kind messages, commands, bots, ordinary non-forward captions, media groups, target/sender mismatches, reversed ids, and distant ids stay separate. Preserve the candidate across session replacement and follower forwarding without weakening exact routing-generation authority.
|
|
122
122
|
- `telegram_voice` and `telegram_button` are not Pi tools; keep prompts/docs explicit that agents should author markup while voice synthesis provider extensions own TTS/OGG conversion, and pi-telegram owns button routing plus Telegram delivery
|
|
123
123
|
- Voice reply policy and prompt context are owned by pi-telegram's `telegram.json` `voice.replyMode`: missing, invalid, `hidden`, and legacy `manual` config resolve to `hidden` and add no `[voice]` context; `mirror` adds `[voice] delivery: automatic voice` only for voice/audio input, while `always` adds that same single factual line to every Telegram turn. Place voice context after `[outputs]` when handler output exists, otherwise after `[attachments]`; provider prompt contributions are optional provider-specific additions, not the default policy or delivery-composition channel.
|
|
124
|
-
- Optional
|
|
124
|
+
- Optional Telegram wall-clock context may add `[time] YYYY-MM-DD HH:mm:ss <timezone>` to Telegram-originated prompts. It is hidden by default, uses `assistant.timeInjection` values `hidden|always|interval`, stores the interval duration separately as `time.interval` in milliseconds, uses the system timezone, and should render last after `[attachments]`, `[outputs]`, and `[voice]` sections. The Settings row `🕒 Time injection: hidden|always|interval` controls only `assistant.timeInjection`; the old `time.injectionMode` key is ignored and is not migrated.
|
|
125
125
|
- Agent activity status is native-only for the release path: Telegram uses `sendChatAction(typing)` and renders its native `…typing` indicator as the automatic work signal. When a connected instance starts any real agent run, send that indicator to the instance's own thread (and aggregate `All` surface), including Telegram turns, local/TUI prompts, and autonomous continuations such as Grow Loop; stop it at agent/session completion. Keep at most one chat action in flight per loop, coalesce identical leader-side actions across follower traffic, and treat Telegram 429 `retry_after` as a shared suppression window rather than a delayed retry or persistent terminal error. This signal is independent from terminal `Active`, which remains scoped to Telegram-owned turns. Confirmed manual `/compact` and automatic compaction tied to an active Telegram turn also use native activity. Do not send activity merely for startup/connect/reload/recovery or add extra in-chat work surfaces without a new explicit design decision.
|
|
126
126
|
- Voice reply mode Settings UI standard: the top-level Settings row is `👄 Voice reply: hidden|mirror|always`; `hidden` is the true default, persists no `voice.replyMode`, adds no voice context, and still allows explicit agent-authored `telegram_voice`; `mirror` activates automatic voice context only for voice/audio input, and `always` activates it for every Telegram turn. The submenu title is `👄 Voice reply mode:`; choice buttons use lowercase labels with a model-style active dot (`🟢 hidden`, `🟢 mirror`) rather than per-mode emoji; the explanatory submenu body uses compact HTML-code bullets such as `<code>-</code> <code>hidden</code> (default): ...`. Preserve this wording/icons unless the operator explicitly asks to redesign it
|
|
127
127
|
- Outbound voice delivery is one fallback pipeline: configured `outboundHandlers` with `type: "voice"` run first in `telegram.json` order, then programmatic voice handlers, then registered voice synthesis providers as zero-config progressive fallbacks; provider extensions must not override operator-configured handlers
|
package/BACKLOG.md
CHANGED
|
@@ -2,24 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
_This backlog tracks only open release-relevant work: hotfixes, bounded maintenance, live runtime verification, evidence-gated Telegram client follow-ups, and upstream Pi API blockers. Completed outcomes and validation evidence belong in `CHANGELOG.md`, not in this queue._
|
|
4
4
|
|
|
5
|
-
## P1 —
|
|
5
|
+
## P1 — Configurable Activity Live Smoke
|
|
6
6
|
|
|
7
|
-
Context:
|
|
7
|
+
Context: `0.25.0` shipped quiet-by-default configurable activity verbosity. The remaining work is live Telegram client verification of verbose reasoning and compact technical tool activity.
|
|
8
8
|
|
|
9
9
|
Bot API evidence: Bot API 10.2 exposes explicit outgoing Rich Message blocks. `InputRichBlockThinking` is draft-only and disappears when the draft lifecycle ends; `InputRichBlockDetails` provides a collapsed disclosure container, and `InputRichBlockPreformatted` provides nested `<pre><code>` content. Exact mobile/Desktop rendering, edit behavior, and practical limits remain live-verification gates.
|
|
10
10
|
|
|
11
11
|
Open work:
|
|
12
12
|
|
|
13
|
-
- [x] Add a persisted `Activity` Settings option backed by `assistant.activity`, with exactly two initial values: `quiet` (default/current behavior) and `verbose` (reasoning plus tool activity). Omitted or invalid configuration must resolve to `quiet`; legacy `assistant.activityVerbosity` is read only when the canonical key is absent and removed on the next write. Settings must follow the existing nested list-option UI and callback/persistence concurrency contracts.
|
|
14
|
-
- [x] In `verbose`, stream reasoning content exposed by supported Pi lifecycle events through a short-lived Telegram Rich Draft `Thinking` block bound to the exact chat/thread, profile, session generation, and direct-owner or follower authority. Finalization, cancellation, failure, replacement, and disconnect must clear local draft ownership without creating a persisted reasoning message or projecting unavailable provider-private chain-of-thought.
|
|
15
|
-
- [x] Persist completed tool activity as bridge-owned compact ordinary HTML messages rather than assistant Markdown or Rich Messages. Render each tool call under one registered semantic icon and tool name with bounded, escaped arguments, updates, result, and error evidence inside a standard expandable blockquote.
|
|
16
|
-
- [x] Coalesce consecutive tool calls from one ordered run into the same editable ordinary message while it remains within Telegram HTML message limits, with one independent expandable blockquote per call. Start a new message when the current message cannot safely accept another block; never reorder tools across assistant prose, reasoning boundaries, targets, runs, or lifecycle generations.
|
|
17
|
-
- [x] Define bounded output, redaction, truncation, retry, edit-ambiguity, and transport-failure behavior so verbose mode cannot leak known secrets, flood the target, replay a possibly committed message, block Pi lifecycle completion, or disturb quiet-mode preview/final ordering. Record failures through existing diagnostics.
|
|
18
|
-
- [x] Reconcile the current no-hidden-reasoning invariant and Rich rendering boundary in `AGENTS.md`, then update README, Settings/configuration docs, architecture/outbound docs, public activity guidance, UI emoji registry, and focused config/menu/activity/delivery/rendering/integration regressions.
|
|
19
|
-
- [x] Order first-level `Settings` by operator meaning while keeping `⬆️ Main menu` first: message presentation (`Draft previews`, `Rendering`, `Voice reply`), technical/public activity (`Activity`, `Proactive push`), prompt context (`Time injection`), then lifecycle (`Thread cleanup`). Append extension-provided rows using their explicit `settings.order` and stable section-id tie-break, preserve each detail submenu's semantic option order, and add focused menu/section regressions.
|
|
20
13
|
- [ ] Live-smoke both modes in classic and Threaded Mode on Telegram mobile and Desktop, including multiple sequential tools, oversized output rollover, cancellation during reasoning, tool failure, session replacement, follower transport, and final-answer ordering.
|
|
21
14
|
|
|
22
|
-
Done when:
|
|
15
|
+
Done when: live mobile and Desktop clients confirm the intended quiet/verbose draft, disclosure, routing, rollover, cancellation, failure, and final-answer ordering behavior in classic and Threaded Mode.
|
|
23
16
|
|
|
24
17
|
## P1 — Native Windows Runtime Smoke
|
|
25
18
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.25.1: Config And Activity Presentation
|
|
4
|
+
|
|
5
|
+
- `Time Injection Config`: Moved the Settings-owned mode to `assistant.timeInjection`, while retaining `time.interval` as the independent interval duration. The old `time.injectionMode` key is ignored without fallback, migration, or automatic rewriting. Impact: assistant presentation controls share one config domain without silently mutating historical user data.
|
|
6
|
+
- `Verbose Tool Evidence`: Quoted argument, update, result, and error labels as JSON-style properties and compacted arrays of object entries to `[{ ... }, { ... }]` while retaining readable fields, evidence order, redaction, truncation, and delivery bounds. Impact: multi-entry tool updates consume less vertical space without losing technical evidence.
|
|
7
|
+
|
|
3
8
|
## 0.25.0: Configurable Activity
|
|
4
9
|
|
|
5
10
|
- `Activity`: Added a persisted quiet-by-default `assistant.activity` Settings control. Verbose mode streams only provider-exposed reasoning through ephemeral target-fenced Rich Thinking drafts, projects inline Markdown bold/code markers to native Rich Text, and records completed executed tools through memory/transport-bounded ordinary HTML messages with standard expandable blockquotes; tool activity never uses Rich Messages. Tool headers use `🛠 <tool>: <status>` with a monospace `running`, `done`, or `failed` status, while argument, update, result, and error labels sit directly before their serialized values (`arguments: {`, `result: {`) for compact scanning. Settings refreshes always read the live value; legacy `assistant.activityVerbosity` is accepted only when the canonical key is absent and removed on the next write. Consecutive ordered tools coalesce without crossing assistant/reasoning, target, activity, or transport-generation boundaries. Final delivery waits behind admitted technical activity inside the background delivery task, while replacement sessions abandon queued old-generation work immediately. Impact: operators can inspect compact redacted technical activity without stale menu state, mixing it into assistant answers, exposing it by default, replaying ambiguous sends, reordering the final answer, or delaying Pi lifecycle completion.
|
package/docs/outbound.md
CHANGED
|
@@ -17,7 +17,7 @@ Proactive blocks use `assistant.rendering` independently of voice policy. Rich m
|
|
|
17
17
|
`assistant.activity` defaults to `quiet`. In `verbose`, the bridge consumes the same normalized Activity stream without mixing technical UI into assistant Markdown:
|
|
18
18
|
|
|
19
19
|
- Provider-exposed reasoning deltas update a bounded ephemeral `sendRichMessageDraft` containing one `Thinking` block. Inline Markdown bold and code markers are projected to native Rich Text so their delimiters do not appear literally; unmatched streaming delimiters remain plain text until a later complete draft. The draft is fenced to its captured chat/thread and transport generation; settlement, cancellation, replacement, disconnect, or authority loss drops local ownership. It is never converted into a persisted reasoning message, and providers that expose no reasoning produce none.
|
|
20
|
-
- Completed executed tools use ordinary `sendMessage` HTML, never `sendRichMessage`. Each tool header renders as `🛠 <tool>: <status>`, with a stable two-space visual gap after the icon and the `running`, `done`, or `failed` status in monospace. One standard Telegram `<blockquote expandable>` then contains bounded redacted evidence in chronological order.
|
|
20
|
+
- Completed executed tools use ordinary `sendMessage` HTML, never `sendRichMessage`. Each tool header renders as `🛠 <tool>: <status>`, with a stable two-space visual gap after the icon and the `running`, `done`, or `failed` status in monospace. One standard Telegram `<blockquote expandable>` then contains bounded redacted evidence in chronological order. JSON-style labels share a line with their serialized value opening, for example `"arguments": {`, `"update 1": {`, and `"result": {` or `"error": {`; arrays of object entries use the denser `[{ ... }, { ... }]` layout while retaining indented fields.
|
|
21
21
|
- Consecutive tools coalesce by editing one message only while target, activity, generation, ordering boundary, tool count, and serialized-size bounds still match. Assistant or reasoning content closes the batch. A non-idempotent send with unknown commit state is never replayed; failed or ambiguous edits start no fallback send.
|
|
22
22
|
|
|
23
23
|
Reasoning retains only a bounded latest-text window, tool updates retain only a bounded latest-entry window, and a session reset abandons queued old-generation work without making the replacement session wait for an old transport call. Final-answer delivery waits for the admitted verbose activity queue inside the extension-owned background delivery task, so completed tool evidence cannot be overtaken by the semantic answer and Pi lifecycle completion remains non-blocking.
|
package/docs/public-api.md
CHANGED
|
@@ -96,13 +96,13 @@ interface TelegramConfig {
|
|
|
96
96
|
rendering?: "rich" | "html";
|
|
97
97
|
proactivePush?: boolean;
|
|
98
98
|
activity?: "quiet" | "verbose";
|
|
99
|
+
timeInjection?: "hidden" | "always" | "interval";
|
|
99
100
|
};
|
|
100
101
|
voice?: {
|
|
101
102
|
replyMode?: "hidden" | "mirror" | "always";
|
|
102
103
|
sendTranscript?: boolean;
|
|
103
104
|
};
|
|
104
105
|
time?: {
|
|
105
|
-
injectionMode?: "hidden" | "always" | "interval";
|
|
106
106
|
interval?: number;
|
|
107
107
|
};
|
|
108
108
|
threads?: {
|
|
@@ -122,7 +122,7 @@ Hidden/default semantics are represented by absence:
|
|
|
122
122
|
- `assistant.activity` accepts exactly `"quiet"` or `"verbose"` and resolves omitted or invalid values to `"quiet"`. Quiet preserves the answer-only behavior. Verbose adds provider-exposed reasoning as an ephemeral target-bound Rich `Thinking` draft. Completed tool activity uses ordinary HTML `sendMessage`/`editMessageText`, with each tool represented by a standard expandable blockquote containing bounded redacted arguments, retained updates, results, and errors; tool output never uses Rich Messages. Consecutive tools coalesce only inside the same ordered activity segment and bounded message. Legacy `assistant.activityVerbosity` is read only when `assistant.activity` is absent and is removed by the next Activity Settings write.
|
|
123
123
|
- Voice Reply `hidden`: no `voice.replyMode` key is persisted; legacy `manual` resolves to this silent default. `mirror` adds `[voice] delivery: automatic voice` only to voice/audio-input turns, while `always` adds the same effective line to every Telegram turn.
|
|
124
124
|
- Agent activity status is not configurable in this release. Telegram uses native `sendChatAction(typing)` / product `...active` status as the only automatic in-chat work signal before the final reply.
|
|
125
|
-
-
|
|
125
|
+
- `assistant.timeInjection` accepts `hidden`, `always`, or `interval` and defaults to `hidden`. Settings writes the selected value there, including `hidden`; the old `time.injectionMode` key is ignored and is not migrated. `time.interval` remains the optional interval duration in milliseconds.
|
|
126
126
|
|
|
127
127
|
With `assistant.rendering: "rich"` (the default), assistant Markdown delivery is native: final replies are sent as `InputRichMessage.markdown` via `sendRichMessage`, and draft previews use `sendRichMessageDraft` when a structurally closed preview frame is available. Draft-frame failures are recorded and skipped rather than converted into raw plain preview messages, because partial Markdown can be temporarily invalid while the final answer remains valid. Long native replies are split at Telegram Rich Message transport limits, with oversized fenced code, display-math, and fully wrapped inline-formatting blocks rewrapped per chunk so persisted chunks remain structurally valid. Guest replies use `InputRichMessageContent` in `answerGuestQuery` results. Bridge-owned UI surfaces such as menus, status, queue controls, commands, and sections keep explicit Telegram HTML/plain rendering by default because those texts are authored by the bridge or companion extensions for Telegram UI. Companion extension sections may explicitly request `"markdown"`, `"html"`, or `"plain"` per view. `assistant.rendering: "html"` keeps the compatibility path that converts assistant Markdown to Telegram HTML before ordinary message delivery. The bridge sets `skip_entity_detection: true` for assistant and guest Markdown so technical text such as `/commands`, hashtags, URLs, phone numbers, and card-like numbers does not gain unintended automatic entities; explicit Markdown links still belong in the Markdown source.
|
|
128
128
|
|
|
@@ -86,11 +86,71 @@ function renderReasoningRichText(text: string): TelegramRichText {
|
|
|
86
86
|
return parts.length === 1 ? parts[0]! : parts;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
function formatActivityJson(value: unknown, depth = 0): string[] {
|
|
90
|
+
const indent = " ".repeat(depth);
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
if (value.length === 0) return [`${indent}[]`];
|
|
93
|
+
if (
|
|
94
|
+
value.every(
|
|
95
|
+
(entry) =>
|
|
96
|
+
entry !== null && typeof entry === "object" && !Array.isArray(entry),
|
|
97
|
+
)
|
|
98
|
+
) {
|
|
99
|
+
const lines = [`${indent}[{`];
|
|
100
|
+
value.forEach((entry, index) => {
|
|
101
|
+
const fields = Object.entries(entry as Record<string, unknown>);
|
|
102
|
+
fields.forEach(([key, nested], fieldIndex) => {
|
|
103
|
+
const nestedLines = formatActivityJson(nested, depth + 1);
|
|
104
|
+
const nestedIndent = " ".repeat(depth + 1);
|
|
105
|
+
lines.push(
|
|
106
|
+
`${nestedIndent}${JSON.stringify(key)}: ${nestedLines[0]!.slice(nestedIndent.length)}`,
|
|
107
|
+
...nestedLines.slice(1),
|
|
108
|
+
);
|
|
109
|
+
if (fieldIndex < fields.length - 1) {
|
|
110
|
+
lines[lines.length - 1] += ",";
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
lines.push(
|
|
114
|
+
index < value.length - 1 ? `${indent}}, {` : `${indent}}]`,
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
return lines;
|
|
118
|
+
}
|
|
119
|
+
const lines = [`${indent}[`];
|
|
120
|
+
value.forEach((entry, index) => {
|
|
121
|
+
const nestedLines = formatActivityJson(entry, depth + 1);
|
|
122
|
+
if (index < value.length - 1) {
|
|
123
|
+
nestedLines[nestedLines.length - 1] += ",";
|
|
124
|
+
}
|
|
125
|
+
lines.push(...nestedLines);
|
|
126
|
+
});
|
|
127
|
+
lines.push(`${indent}]`);
|
|
128
|
+
return lines;
|
|
129
|
+
}
|
|
130
|
+
if (value !== null && typeof value === "object") {
|
|
131
|
+
const entries = Object.entries(value as Record<string, unknown>);
|
|
132
|
+
if (entries.length === 0) return [`${indent}{}`];
|
|
133
|
+
const lines = [`${indent}{`];
|
|
134
|
+
entries.forEach(([key, nested], index) => {
|
|
135
|
+
const nestedLines = formatActivityJson(nested, depth + 1);
|
|
136
|
+
const nestedIndent = " ".repeat(depth + 1);
|
|
137
|
+
lines.push(
|
|
138
|
+
`${nestedIndent}${JSON.stringify(key)}: ${nestedLines[0]!.slice(nestedIndent.length)}`,
|
|
139
|
+
...nestedLines.slice(1),
|
|
140
|
+
);
|
|
141
|
+
if (index < entries.length - 1) lines[lines.length - 1] += ",";
|
|
142
|
+
});
|
|
143
|
+
lines.push(`${indent}}`);
|
|
144
|
+
return lines;
|
|
145
|
+
}
|
|
146
|
+
return [`${indent}${JSON.stringify(value)}`];
|
|
147
|
+
}
|
|
148
|
+
|
|
89
149
|
function serializeActivityValue(value: unknown): string {
|
|
90
150
|
const seen = new WeakSet<object>();
|
|
91
151
|
let text: string;
|
|
92
152
|
try {
|
|
93
|
-
|
|
153
|
+
const normalized =
|
|
94
154
|
JSON.stringify(
|
|
95
155
|
value,
|
|
96
156
|
(_key, nested) => {
|
|
@@ -101,8 +161,8 @@ function serializeActivityValue(value: unknown): string {
|
|
|
101
161
|
}
|
|
102
162
|
return nested;
|
|
103
163
|
},
|
|
104
|
-
2,
|
|
105
164
|
) ?? JSON.stringify(String(value));
|
|
165
|
+
text = formatActivityJson(JSON.parse(normalized)).join("\n");
|
|
106
166
|
} catch {
|
|
107
167
|
text = JSON.stringify(String(value));
|
|
108
168
|
}
|
|
@@ -113,17 +173,17 @@ function serializeActivityValue(value: unknown): string {
|
|
|
113
173
|
}
|
|
114
174
|
|
|
115
175
|
function renderToolActivityHtml(tool: ToolActivity): string {
|
|
116
|
-
const evidence = [`arguments: ${tool.args}`];
|
|
176
|
+
const evidence = [`"arguments": ${tool.args}`];
|
|
117
177
|
if (tool.droppedUpdates > 0) {
|
|
118
178
|
evidence.push(`… [${tool.droppedUpdates} earlier updates omitted]`);
|
|
119
179
|
}
|
|
120
180
|
tool.updates.forEach((update, index) => {
|
|
121
181
|
evidence.push(
|
|
122
|
-
`update ${tool.droppedUpdates + index + 1}: ${update}`,
|
|
182
|
+
`"update ${tool.droppedUpdates + index + 1}": ${update}`,
|
|
123
183
|
);
|
|
124
184
|
});
|
|
125
185
|
if (tool.complete && tool.result !== undefined) {
|
|
126
|
-
evidence.push(
|
|
186
|
+
evidence.push(`"${tool.isError ? "error" : "result"}": ${tool.result}`);
|
|
127
187
|
}
|
|
128
188
|
const status = tool.complete
|
|
129
189
|
? tool.isError
|
package/lib/config.ts
CHANGED
|
@@ -44,7 +44,6 @@ export interface TelegramOutboundHandlerConfig extends CommandTemplateObjectConf
|
|
|
44
44
|
export type TelegramTimeMode = "hidden" | "always" | "interval";
|
|
45
45
|
|
|
46
46
|
export interface TelegramTimeConfig {
|
|
47
|
-
injectionMode?: TelegramTimeMode;
|
|
48
47
|
interval?: number;
|
|
49
48
|
}
|
|
50
49
|
|
|
@@ -76,6 +75,7 @@ export interface TelegramConfig {
|
|
|
76
75
|
rendering?: TelegramAssistantRenderingMode;
|
|
77
76
|
proactivePush?: boolean;
|
|
78
77
|
activity?: TelegramActivityVerbosity;
|
|
78
|
+
timeInjection?: TelegramTimeMode;
|
|
79
79
|
/** @deprecated use activity */
|
|
80
80
|
activityVerbosity?: TelegramActivityVerbosity;
|
|
81
81
|
};
|
|
@@ -834,10 +834,11 @@ function getSystemTimezone(): string {
|
|
|
834
834
|
|
|
835
835
|
export function resolveTelegramTimeConfig(
|
|
836
836
|
raw: TelegramTimeConfig | undefined,
|
|
837
|
+
timeInjection: TelegramTimeMode | undefined = undefined,
|
|
837
838
|
): ResolvedTelegramTimeConfig {
|
|
838
839
|
const injectionMode: TelegramTimeMode =
|
|
839
|
-
|
|
840
|
-
?
|
|
840
|
+
timeInjection === "always" || timeInjection === "interval"
|
|
841
|
+
? timeInjection
|
|
841
842
|
: "hidden";
|
|
842
843
|
const interval =
|
|
843
844
|
typeof raw?.interval === "number" && raw.interval > 0
|
|
@@ -850,13 +851,25 @@ export function resolveTelegramTimeConfig(
|
|
|
850
851
|
export function createTelegramTimeConfigGetter(
|
|
851
852
|
configStore: Pick<TelegramConfigStore, "get">,
|
|
852
853
|
): () => ResolvedTelegramTimeConfig {
|
|
853
|
-
return () =>
|
|
854
|
+
return () => {
|
|
855
|
+
const config = configStore.get();
|
|
856
|
+
return resolveTelegramTimeConfig(
|
|
857
|
+
config.time,
|
|
858
|
+
config.assistant?.timeInjection,
|
|
859
|
+
);
|
|
860
|
+
};
|
|
854
861
|
}
|
|
855
862
|
|
|
856
863
|
export function createTelegramTimeInjectionModeGetter(
|
|
857
864
|
configStore: Pick<TelegramConfigStore, "get">,
|
|
858
865
|
): () => TelegramTimeMode {
|
|
859
|
-
return () =>
|
|
866
|
+
return () => {
|
|
867
|
+
const config = configStore.get();
|
|
868
|
+
return resolveTelegramTimeConfig(
|
|
869
|
+
config.time,
|
|
870
|
+
config.assistant?.timeInjection,
|
|
871
|
+
).injectionMode;
|
|
872
|
+
};
|
|
860
873
|
}
|
|
861
874
|
|
|
862
875
|
export function createTelegramTimeInjectionModeSetter(
|
|
@@ -865,19 +878,12 @@ export function createTelegramTimeInjectionModeSetter(
|
|
|
865
878
|
return async (injectionMode) => {
|
|
866
879
|
await loadLatestTelegramConfig(configStore);
|
|
867
880
|
const current = configStore.get();
|
|
868
|
-
if (injectionMode === "hidden") {
|
|
869
|
-
const { injectionMode: _injectionMode, ...remainingTime } =
|
|
870
|
-
current.time ?? {};
|
|
871
|
-
const next = { ...current };
|
|
872
|
-
if (Object.keys(remainingTime).length > 0) next.time = remainingTime;
|
|
873
|
-
else delete next.time;
|
|
874
|
-
configStore.set(next);
|
|
875
|
-
await configStore.persist(next);
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
881
|
const next = {
|
|
879
882
|
...current,
|
|
880
|
-
|
|
883
|
+
assistant: {
|
|
884
|
+
...(current.assistant ?? {}),
|
|
885
|
+
timeInjection: injectionMode,
|
|
886
|
+
},
|
|
881
887
|
};
|
|
882
888
|
configStore.set(next);
|
|
883
889
|
await configStore.persist(next);
|