Commit Graph

117 Commits

Author SHA1 Message Date
EugeneTes
b49b3eea00 Merge branch 'feat/telegram-attachments' 2026-09-26 22:37:40 +02:00
EugeneTes
6ab98a8155 fix(telegram): address review findings
- A speech or download timeout (TaskCanceledException nobody asked for) is a failure
  with the user notice, not a silent shutdown; OpenAiSpeechToText maps it to
  "the request timed out."; UpdateRouter only treats the caller's cancellation as shutdown.
- send: WorkspacePaths resolves the real path component by component (realpath),
  closing the directory-link-inside-target bypass; `.` reports a directory.
- Image documents are attached inline only when their magic bytes are JPEG/PNG/GIF/WebP,
  under the detected type.
- Audio goes to STT as an ASCII audio.<ext> name.
- MCP Browser section mentions send only when it is active.
- RunInOrderAsync takes a cancellation token; queued work is skipped once disposal gives up;
  SendReplyAsync stays private behind ReplyInOrderAsync.
- Speech HttpClient handler recycles pooled connections; BuiltInTools.Send();
  IExtensionHost.Channel is abstract; WorkspacePaths in its own file.
- Tests: timeouts, symlink chains (Linux), sniffing, channel delivery failures,
  queued-work cancellation, gated voice ordering. Spec and docs updated.
2026-09-26 22:35:20 +02:00
EugeneTes
c5ecd017a0 docs(telegram): attachments, voice, send and IChatChannel
telegram-bot.md: what you can send, files from the agent, speech settings, security notes. ui-integration.md: document/voice input rows and a new section 5 on IChatChannel. architecture, extensions and CLAUDE.md list IChatChannel. Drop the interim paragraph from the message-context spec; mark the attachments spec implemented with resolved questions.
2026-09-26 22:16:29 +02:00
EugeneTes
f90731baf0 feat(telegram): transcribe voice notes and audio with an echo
Content step 3 (InboundMedia.AddSpeechAsync): voice and audio go through ISpeechToText, the transcript is echoed as '🎤 …' and prompted as '[Voice message] …'. Spec §4 edited: audio is transcribed from the file step 2 saved (one download); an oversize or undownloadable voice note sends its notice and isn't prompted.
2026-09-26 22:13:10 +02:00
EugeneTes
a0cd2975b8 feat(telegram): save documents, videos and audio to the chat inbox
Content step 2 (InboundMedia.AddFilesAsync, InboxFiles): files land in <cwd>/inbox/<date>-<safe name>[-N], image documents within Telegram:InlineImageMaxBytes are attached inline, oversize files and failed downloads get a notice and no run. Documents, videos, audio and voice are accepted; the unsupported reply is now 'I can read text, photos, files and voice messages.'

Spec: Updates helpers are named DocumentMessage/VideoMessage/VoiceMessage/AudioMessage; the getFile 'file is too big' notice reads 'over 20 MB'; the too-large inline note is a parenthesised suffix after the size; size formats spelled out.
2026-09-26 22:09:16 +02:00
EugeneTes
8758f4b3d1 feat(telegram): OpenAI speech to text service and speech options
Spec 5: the HttpClient registration is named as AddHttpClient<ISpeechToText, OpenAiSpeechToText>() (package Microsoft.Extensions.Http), which is how Program.cs registers it.
2026-09-26 22:04:27 +02:00
EugeneTes
ee3049e47a feat(telegram): TelegramChatChannel delivers send files and scheduler texts in order
Add TelegramLimits, TelegramChatChannel (every delivery through
ChatRenderer.RunInOrderAsync, auto photo falls back to a document on a 400)
and wire it into ChatRegistry as AgentSessionOptions.Channel and
ChatContext.Channel. Spec: SendTextAsync names its failure text
("Telegram did not accept the message.") and throws when any chunk fails.
2026-09-26 22:00:16 +02:00
EugeneTes
1eed03e851 feat(telegram): ordered renderer work items and file uploads 2026-09-26 21:56:05 +02:00
EugeneTes
b22d0d159e feat(core): register send for sessions with a chat channel 2026-09-26 21:52:22 +02:00
EugeneTes
7dce84ddc5 feat(core): send tool delivers workspace files through IChatChannel
SendTool (Tool<SendArgs>, sequential) confines the path to the workspace including symlinked files and parent directories (WorkspacePaths), checks caption, photo and document limits before sending, and picks Document for non-photo extensions (.gif included) or oversized images under kind auto. Not registered yet; that is the next task.
2026-09-26 21:49:02 +02:00
EugeneTes
f2d6b7e5dc feat(core): IChatChannel contract and AgentSessionOptions.Channel
Adds the host-agnostic IChatChannel contract (ChatFile, ChatDelivery, ChatChannelLimits, ChatDeliveryException) to Abstractions/Ui, AgentSessionOptions.Channel, AgentSession.Channel and IExtensionContext.Channel. IExtensionHost.Channel is an internal plumbing default member that ExtensionContext forwards. Test support: FakeChatChannel and the TestHost builder's Channel.
2026-09-26 21:45:57 +02:00
EugeneTes
a21f3be733 docs(plan): telegram-attachments implementation plan 2026-09-26 21:43:04 +02:00
EugeneTes
0b28b9627d Merge branch 'feat/google-calendar' 2026-09-26 21:24:34 +02:00
EugeneTes
4cfe3cec47 Merge branch 'feat/mcp-client-playwright' 2026-09-26 21:20:52 +02:00
EugeneTes
e726b7c220 Merge branch 'feat/telegram-message-context' 2026-09-26 21:20:40 +02:00
EugeneTes
2a201f7d95 fix(mcp): address review findings
- redact /mcp output, and stderr lines as they arrive (log, errors, tail)
- take host servers by name: configuration binding sorts keys anyway
- dispose kills the tree only without an exit code and only if the pid's
  start time matches the spawn (no kill of a reused pid)
- idle timer generation guards stale callbacks; catalogue check runs in
  the background with a 10 s cap; shutdown stops servers concurrently
- stop resets a failed status; /mcp shows an exited process; ordinal names
- spec: ${workspace} in command, ordering, dispose and /mcp wording
2026-09-26 21:18:12 +02:00
EugeneTes
c9fd58372a docs(mcp): MCP client and Playwright preset 2026-09-26 21:05:17 +02:00
EugeneTes
7900b26e28 test(mcp): give the hang-on-start discovery test a 3 s startup timeout
The fake logs its start only after the .NET runtime is up; under full-suite
load that took over 1 s, the client killed it first and the start entry was
missing.
2026-09-26 21:05:16 +02:00
EugeneTes
4fb8f39ab4 feat(telegram): MCP servers and the Playwright preset in the bot 2026-09-26 21:05:16 +02:00
EugeneTes
963301efc4 test(mcp): environment, redaction and end to end through the core
The TZ test checks TZ is absent on Windows: the process policy never sets TZ there (process-environment-hook spec §3). Spec testing bullet and plan updated to match.
2026-09-26 20:54:20 +02:00
EugeneTes
2c5d5dfcb4 feat(mcp): extension activation, prompt section, lifecycle and /mcp
Spec: /mcp shows no pid (the SDK doesn't expose it) and never shows env values; the prompt section follows the server's own tools; the built-in collision check covers inactive built-ins.
2026-09-26 20:49:21 +02:00
EugeneTes
83ad683bd1 feat(mcp): per-session server connection and MCP tools
McpServerConnection starts the session's server lazily through
PrepareProcessAsync, applies the call timeout, reports exits with the
stderr tail and restarts on the next call, stops when idle, and supports
StopAsync (reusable) and idempotent DisposeAsync. McpTool exposes a
catalogue entry as an ITool and maps results with McpResultMapper.

Spec change (§5, Decisions): ModelContextProtocol.Core 2.2.0 does not send
notifications/cancelled when a request's token fires (verified against the
fake server), so calls go out through SendRequestAsync with their own
request id and the connection sends notifications/cancelled itself on abort
and timeout. Also documents exit detection, StopAsync vs DisposeAsync and
JSON-RPC errors as ordinary error results, per the plan.
2026-09-26 20:44:15 +02:00
EugeneTes
ed026ac6c8 feat(mcp): process-wide tool catalogue discovery
McpProcess starts a server through the SDK stdio client under the startup
timeout and keeps a stderr tail for its errors. McpCatalogCache runs one shared
discovery per server config in a temp dir, with the environment from the process
spawn policy. It keeps successes for the process lifetime and failures for 60 s.

Deviation from the plan: the discovery process is disposed and its temp dir
deleted in the background, after the catalogue is returned. SDK 2.2 waits the
whole ShutdownTimeout (5 s) with stdin still open before it kills the process,
so an awaited dispose held every first activation for 5 s. A new test
(Discovery_returns_without_waiting_for_the_server_shutdown) covers this. Spec
section 3 gets the new GetAsync signature, the detached discovery and the
background cleanup. Section 5 now describes the SDK's shutdown order correctly.
2026-09-26 20:31:43 +02:00
EugeneTes
881e7f0be9 fix(google-calendar): address review findings
- log an unknown stored zone once even when the tool description reads it first
- serialise the flow's token write with /calendar_disconnect under the flow gate
- a cancelled or expired pending flow is cleared, not re-sent or shown
- chat keys anchor with \z so a trailing newline is hashed
- a timeMax date includes that whole day (spec updated)
- dispose the flow's CancellationTokenSource, drop ConnectFlow.Token, Begin is a plain write
- GoogleCalendarOptions.IsConfigured replaces the host's copy of the rule; StoredToken is internal
- test BotSetupService's menu through DI, tokens absent from tool results
- Telegram.Tests csproj reference placed as in telegram-message-context; ChatHost passes calendar by name
- docs: per-event timeZone, both config sources for /calendar_*
2026-09-26 20:30:56 +02:00
EugeneTes
52f9d88575 test(mcp): fake stdio MCP server
Spec: the fake uses the SDK's server API from ModelContextProtocol.Core and gains the tools and modes the tests need (weird_name_x, a >48-char name, builtin-clash, --output-dir browser tools, die message and exit code, count-starts details, slow cancellation log).
2026-09-26 20:24:42 +02:00
EugeneTes
8ece6f303d docs(google-calendar): setup guide, Telegram configuration and architecture notes
Spec: the unverified-app screen is described in text; open question 1 resolved (full calendar scope); status implemented.
2026-09-26 20:20:32 +02:00
EugeneTes
fb314a5d87 docs(mcp): align plan's truncation note separator with the mapper 2026-09-26 20:19:17 +02:00
EugeneTes
da6068c5dc feat(mcp): map MCP tool results to core tool results
The truncation note follows the kept text after a single newline, as the built-in read/grep tools do (plan snippet updated).
2026-09-26 20:19:03 +02:00
EugeneTes
a635e9761b feat(telegram): Google Calendar per chat (config, secret value, menu, help)
Spec §8: extension added when either value is set, menu and /help only when both are; host code in GoogleCalendarSetup.cs.
2026-09-26 20:18:00 +02:00
EugeneTes
b30c71d30a feat(mcp): server launch plumbing and tool naming
Spec: the complete environment goes to the SDK with InheritEnvironmentVariables=false instead of nulls; the cache key also covers cwd and prefix.
2026-09-26 20:17:37 +02:00
EugeneTes
2ec941bf2a fix(telegram): address review findings
- /resume discards the inbox once the id names one conversation, before any await
- /stop discards input still waiting in the window
- a forwarded message is never a command or a pending answer
- ChatInbox.DrainAsync is an internal test helper
- tests: /resume and /stop drop buffered text, forwarded /new is content,
  a failing flush doesn't stop later batches; no wall-clock sleep in the drain test
- spec and user docs: timestamp exception for extension commands, command
  ordering, InputDebounce=0 wording
2026-09-26 20:15:57 +02:00
EugeneTes
5b7d593427 feat(mcp): Playwright preset and server resolution
Spec: the preset defaults --browser to chromium and drops --user-data-dir with --isolated; source order and the send-less prompt line are spelled out.
2026-09-26 20:15:35 +02:00
EugeneTes
149b932ca1 feat(google-calendar): /calendar_status and /calendar_disconnect, per-chat isolation tests 2026-09-26 20:15:16 +02:00
EugeneTes
e6b09fef1e feat(mcp): extension project and configuration
McpOptions/McpServerConfig/PlaywrightOptions with ApplyEnvironment (PLAYWRIGHT_* variables, quote-aware ExtraArgs split) and Validate; ShellWords; mcpServers from the user settings.json. New project in the solution and referenced by the extension tests.
2026-09-26 20:13:50 +02:00
EugeneTes
59d404ed08 feat(google-calendar): /calendar_connect device flow with automatic polling 2026-09-26 20:13:36 +02:00
EugeneTes
aaca3ad15a refactor(tools): move ToolExecutionException to Abstractions 2026-09-26 20:12:19 +02:00
EugeneTes
c2048a822e feat(google-calendar): create, update and delete with recurring scope resolution
Spec §7: the reference's calendar.ts is not in this repository, so the error and reply texts are now listed in the spec.
2026-09-26 20:11:47 +02:00
EugeneTes
f39b8a84c1 docs(plan): mcp-client-playwright implementation plan 2026-09-26 20:10:36 +02:00
EugeneTes
edcfa690bd feat(google-calendar): calendar tool read actions over Calendar v3 2026-09-26 20:09:53 +02:00
EugeneTes
0c1778e741 docs(telegram): message context, grouping and presence
InputDebounce config row, the Using it section (replies, quotes, forwards, local-time header, grouping window, eyes reaction, online message, forwards never count as the login code), the Inbound paragraph in CLAUDE.md, and spec status set to implemented.
2026-09-26 20:09:20 +02:00
EugeneTes
a30a4e4d03 feat(google-calendar): calendar time rules and rendering
Offset date-times are normalised to full RFC 3339 (seconds, T separator)
before they are sent; the spec's time rules say so.
2026-09-26 20:08:10 +02:00
EugeneTes
98faaf3a12 feat(telegram): send 🟢 Online. to the owner once after bot setup 2026-09-26 20:07:58 +02:00
EugeneTes
c3f1bbd463 feat(telegram): react to steering messages, discard the inbox on /new and /resume
A flushed batch that joins a running request gets a 👀 reaction on each message. /new and a successful /resume drop buffered and in-flight inbox parts. A forwarded message never answers a pending TelegramUi question. /help mentions replies and quotes.
2026-09-26 20:06:43 +02:00
EugeneTes
8baa79f074 feat(google-calendar): per-chat connections with cached, single-flight token refresh
Spec §4: connections are keyed by the chat's token directory; For(settings, scope).
2026-09-26 20:06:00 +02:00
EugeneTes
7bd3bb340c feat(telegram): route messages through the chat inbox with context headers
Spec: unhandled /commands bypass the inbox verbatim; the header zone comes from ChatRegistry.TimeZoneFor (session zone until ChatSettingsStore); only text and photos until the attachments spec.
2026-09-26 20:04:06 +02:00
EugeneTes
ccfa3c1ade feat(google-calendar): OAuth device flow, refresh and revoke client
Spec §3: DeviceAuthorization/PollStatus shapes, Success carries the access token, 5xx without error is a transport error, RefreshAsync returns AccessToken, RevokeAsync returns the failure reason (null on success).
2026-09-26 20:03:09 +02:00
EugeneTes
199ad47f64 feat(google-calendar): extension project, options and per-chat token store
Adds the GoogleCalendar extension project (Abstractions only, Private=false), GoogleCalendarOptions with
env fallbacks and a secret-free ToString, GoogleCalendarException/Messages, ChatKeys (scope -> safe directory
name), StoredToken and GoogleTokenStore (atomic write, owner-only on Unix, reads never throw), and an empty
GoogleCalendarConnections stub for Task 3. The test project gains the extension reference and
Microsoft.Extensions.TimeProvider.Testing 10.9.0.
2026-09-26 20:01:18 +02:00
EugeneTes
5a8c4e6b4b feat(telegram): inbound composer with the text and photo content step 2026-09-26 20:00:23 +02:00
EugeneTes
6b3ee82489 docs(plan): google-calendar implementation plan 2026-09-26 19:59:30 +02:00
EugeneTes
7eb9f84faa feat(telegram): InputDebounce option and message reactions 2026-09-26 19:58:55 +02:00