@glows777/vela-mcp 0.0.0-stage → 0.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/CHANGELOG.md +263 -0
- package/LICENSE +21 -0
- package/README.md +4 -2
- package/dist/config.d.ts +79 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +226 -0
- package/dist/config.js.map +1 -0
- package/dist/connection.d.ts +61 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +248 -0
- package/dist/connection.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -0
- package/dist/tools.d.ts +40 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +167 -0
- package/dist/tools.js.map +1 -0
- package/package.json +38 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@glows777/vela` are listed here. While Vela is on 0.x, a release that breaks the public API bumps the minor version and lists the break under **Breaking Changes** with how to migrate; patch releases don't break anything. See [API stability](docs/sdk.md#api-stability) for what counts as public.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.5.0] - 2026-11-04
|
|
8
|
+
|
|
9
|
+
### Breaking Changes
|
|
10
|
+
|
|
11
|
+
- **Feishu identifiers are now Lark throughout Vela.** Import `lark()` and the corresponding `Lark*` types from `@glows777/vela-lark` or `@glows777/vela`. Replace `builtin:feishu`, `extensionConfig.feishu`, `feishu_*` tools/loadouts/permission references, prompt keys `feishuChannel`/`feishuTopic`, `FEISHU_APP_ID`/`FEISHU_APP_SECRET`/`FEISHU_OWNERS` and `WAKE_FEISHU_VELA_WEBHOOK_URL` with their Lark equivalents. Old public aliases are removed. Stop ingress and follow the [dry-run-first migration guide](docs/lark-migration.md) before restarting an existing deployment. Routing changes preserve physical session IDs, JSONL, original workspace/memory/attachment paths and append-only usage. Collisions and orphaned legacy state stop for explicit resolution. Upstream SDK/domains/events/regions and historical evidence remain unchanged; this is not a provider-region migration.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Topics and the group session hand off work, like threads and the coordinator in Claude Projects ([#241](https://github.com/glows777/Vela/issues/241)). `channel_message_topic` sends a note from any group session to another owned topic, and `channel_message_group` sends one from a topic to the group session. Notes are queued like other channel input, never steer, post nothing in the chat, and a chain stops after three hops. A finished, waiting or stopped topic adds a status line to the group session's next run without a model call. A new topic starts with its title, requester, shared workspace files and the requester's original message verbatim. Topics can list topics and read another topic with `lark_read_topic`. `IncomingMessage.handoff` and `OutgoingMessage.handoff` are new. A note still queued when Vela restarts is dropped.
|
|
16
|
+
|
|
17
|
+
- Channel conversations can manage durable `channel_subscriptions`; the channel-aware SDK publishes untrusted external events through the existing queue, with logical-topic routing, optional existing-chat fallback and final-event cleanup. Generic channel tools use `ToolDefinition.channel: true` without exposing them to ordinary SDK sessions.
|
|
18
|
+
External event admission/announcement failures are diagnosed without replying to the preceding human message; CLI and SDK default event targets normalize surrounding whitespace.
|
|
19
|
+
|
|
20
|
+
- Opt-in channel webhook ingress binds only to `127.0.0.1`, verifies GitHub HMAC signatures over at most 1 MiB of raw bytes, and remembers the last 1,000 delivery IDs in memory. Allowlisted check suites, submitted reviews, PR conversation comments and closure events wake subscribed conversations through the existing untrusted-event queue. Self-origin comments/reviews are skipped; tunnel setup remains external. See [Channels](docs/channels.md#github-webhooks).
|
|
21
|
+
|
|
22
|
+
- Generic shared-secret events reuse the same receiver, untrusted queue and final cleanup. The host fixes one source and its `source:` key namespace; raw-body HMAC, strict payload validation and adapter-scoped delivery IDs prevent source/target injection and cross-adapter dedup collisions. GitHub and generic may run separately or together with independent secrets. See [signed local-script example](docs/channels.md#generic-shared-secret-events).
|
|
23
|
+
|
|
24
|
+
- Experimental frontend helpers in `@glows777/vela-core/diagnostics` share core session rendering, validation and trust rules with the CLI. Recording resolves models without constructing an extra Vela; exact public development mappings make focused tests and the watched CLI observe source edits immediately, while builds and packed consumers retain real artifact resolution.
|
|
25
|
+
|
|
26
|
+
- Session routing options (`scope`, `channel`), read-only scope and tool-call session identity, `toolDecision`, explicit `save({ reportFailure })`, lifecycle callbacks, channel-only event publication and experimental diagnostic snapshots support core/channel separation. Existing package root and testing imports remain compatible.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- **The interactive TUI keeps a fullscreen transcript viewport.** New output is followed while the viewport is at the bottom; scrolling up holds the reading position and shows `↓ New output · scroll to bottom`. Scroll back to the bottom or press Ctrl+End to return to the latest output and resume following. See [CLI](docs/cli.md#interactive-mode).
|
|
31
|
+
|
|
32
|
+
- **`write_file` no longer overwrites an existing file the session has not read to the end or written itself.** The session must have read the file through to EOF with `read_file`, or written it with `write_file` / `edit_file`, and the file must not have changed on disk since; the refusal asks for a full read or a targeted `edit_file`. The record is per session and in memory, so a resumed session reads again. New files and `edit_file` are unchanged. See [Tools](docs/tools.md#write_file).
|
|
33
|
+
|
|
34
|
+
- Six jointly versioned Bun workspace packages separate core SDK, channel runtime/interfaces, Lark transport, MCP, RAG and the umbrella CLI. Existing `@glows777/vela` and `/testing` imports, `vela` executable and non-adapter behavior remain compatible; the separately listed Lark rename is breaking. Lightweight consumers can install `@glows777/vela-core` or `@glows777/vela-channels`; each package has an explicit export map, declared runtime dependencies and API snapshot.
|
|
35
|
+
|
|
36
|
+
- Opening an existing session may establish its first routing binding; matching bindings are accepted and conflicting routes throw `SessionRoutingConflictError` with the session ID and both routes. Existing cwd, tools, permissions and model options remain first-open-only. Forks and clones do not inherit routing authority.
|
|
37
|
+
- Save-success observer failures are logged without replaying already committed history. Ordinary `save()` still flushes and rejects on storage failure without emitting a failure event; only `reportFailure: true` emits the existing event.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **Windows shell and clipboard handling.** The CLI selects the real Git for Windows `bin\\bash.exe` wrapper instead of `.cmd` shims; the SDK defaults to `bash.exe` on `PATH` unless `shellPath` is set. Bash cancellation kills child processes. If Bash cannot start, Vela disables it for the session and enables read-only discovery tools in default sessions. `/copy` uses PowerShell to read UTF-8 text from a temporary file, and clipboard image reads that finish after TUI shutdown are ignored.
|
|
42
|
+
- Changing the thinking level during a run now applies from the next model request instead of waiting for the next prompt ([#238](https://github.com/glows777/Vela/issues/238)).
|
|
43
|
+
|
|
44
|
+
## [0.4.0] - 2026-10-27
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- **`vela serve` runs the configured channels without the TUI.** The first argument `serve` selects a new run mode: no terminal UI and nothing on stdout, one line per channel event on stderr with a local timestamp (`channel_message`, `channel_reply`, `channel_error`, terminal escape sequences stripped and text cut to 80 characters), and SIGINT/SIGTERM stop the channels through the existing `dispose()` and exit 0. A second signal exits right away. `serve` takes no prompt; `-p`, `--mode`, `--export`, `--list-models`, session selection (`-c`, `-r`, `--session`, `--fork`), tool flags (`-t`, `-nt`, `-xt`) are rejected with exit code 2 instead of being ignored. When no channel started (none configured, or every `start()` failed), it exits 1 instead of idling. See [CLI](docs/cli.md#serve) and [Channels](docs/channels.md).
|
|
49
|
+
|
|
50
|
+
- **Built-in `permission-gate` extension, off by default.** It confirms a few dangerous `bash` patterns (recursive force-deletes, `sudo`, `chmod`/`chown` 777) through the session UI and blocks them when there is no UI, like pi's `permission-gate` example. Enable it with `+builtin:permission-gate` in settings or `-e builtin:permission-gate`; the SDK adds `permissionGate()`. `loadConfig` gains `defaultOffBuiltins` for callers that pass `builtins` themselves. See [Built-in extensions](docs/built-in-extensions.md#permission-gate).
|
|
51
|
+
|
|
52
|
+
- **Passive Feishu group messages show a reaction once Vela starts answering.** An untagged evaluation adds `OnIt` to the triggering message at the first non-empty assistant text delta, then `DONE` after delivery or `CrossMark` on failure or Stop. Silence, an empty answer or a topic handoff removes the marker with nothing left behind; passive input still gets no progress/control card. Custom channels receive the new `silent` value on `finish(outcome)`.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- **The TUI no longer starts channels.** Channels run only under `vela serve`, so an interactive `vela` session no longer opens a second connection to the chat service or a second writer to the single-process channel directory, and channel messages no longer appear in the chat log. The SDK is unchanged: `vela.startChannels()` still starts them in your own process. See [Channels](docs/channels.md) and [CLI](docs/cli.md#interactive-mode).
|
|
57
|
+
|
|
58
|
+
- **The core prompt has output rules, an `<env>` block, and Feishu group history carries send times.** The new rules ask for the answer first, a length that matches the question, no restating and no unrequested next steps, and say something was saved, sent or done only after the tool call for it succeeded. `<env>` adds the date (not the time of day, so the cached prompt prefix only changes once a day), time zone, OS, shell and git branch; the branch comes from the session working directory's `.git/HEAD`, with no subprocess. Each `[Chat since last reply]` and `[Question]` line now starts with the message's local `HH:MM`.
|
|
59
|
+
|
|
60
|
+
- **The core prompt's length rule is concrete, and the separate concision rule is gone.** "Match the response length to the question" now asks for a short paragraph plus at most three bullets, the main path and the facts asked for, not every branch, option, test or per-step file:line, and no closing restatement. This replaces "Be concise and direct in your responses.", which said the same thing less clearly, so the core rules are nine instead of ten. This is a prompt change: there is no length check in code.
|
|
61
|
+
|
|
62
|
+
- **Only four core tools are on by default (like pi).** A new session declares `read_file`, `write_file`, `edit_file` and `bash` (plus `tool_search`); `grep`, `find` and `list_directory` stay registered but off, so models that search with `bash` no longer pay for their schemas. Enable one with `--tools +grep` (or `--tools +grep,+find,+list_directory`) or a list of only `+name` / `-name` entries in the new `defaultTools` setting; plain names still select exactly those tools, and a project's `defaultTools` entries adjust the user list. The SDK `tools` option takes the same entries. Tools that extensions register are unaffected. See [Tools](docs/tools.md), [Settings](docs/settings.md#defaulttools) and [CLI](docs/cli.md).
|
|
63
|
+
|
|
64
|
+
- **Feishu tools and startup stay inside Feishu.** The seven `feishu_*` tools are no longer declared to plain CLI or SDK sessions: `ToolDefinition.channel` names the channel that owns a tool, and a session opened for another channel (or no channel) hides and denies it. Group and topic sessions keep their existing tool sets. A Feishu channel without `appId`/`appSecret` now returns `false` from `start()`, so the gateway skips its startup line, routines and recovery instead of printing `✓ feishu started`.
|
|
65
|
+
- Extension tool registration rejects non-Zod input schemas immediately with the extension and tool names. The CLI reports the failed extension and continues loading other extensions; internal MCP JSON schemas remain supported.
|
|
66
|
+
|
|
67
|
+
- **A Feishu topic opens with a short title, and the brief stays private.** `feishu_start_topic` takes `title` and `brief` instead of one `task`: the title is posted as the topic's first visible message and becomes its label in `feishu_list_topics`, while the brief goes only to the topic session with the sender's name. The channel prompt asks for a one-line title and keeps the plan out of both texts. Custom `FeishuTransport` implementations receive the title in `startTopic`; `ChannelRuntime.dispatch` takes an optional label for the directory entry.
|
|
68
|
+
|
|
69
|
+
- Compaction now produces a structured free-text checkpoint with progress, next steps and code-derived read/modified file lists. Summaries use the model output limit instead of a fixed 8,192-token cap; incomplete or failed summaries leave history unchanged.
|
|
70
|
+
|
|
71
|
+
- **Tool results sent to the model are plain text.** Bash no longer adds a timestamp or a `StoredToolResult` JSON wrapper; only truncated output includes a full-output path. Default and web result limits are now 50K characters, with head previews for generic/web results and the existing bash tail. The system history guide is removed; compaction keeps the earlier-output location. Result files and crash-recovery history are retained, and old `StoredToolResult` sessions still load. SDK consumers reading tool messages should handle text results instead of expecting `StoredToolResult`.
|
|
72
|
+
|
|
73
|
+
- **Memory and knowledge-base context arrives as messages after the cached prefix.** The memory index and the RAG source list are appended when they change instead of being rebuilt into the system prompt; the fixed memory usage rules stay there. Saving a memory, deleting entries or ingesting a document no longer rewrites the cached prompt prefix. Loading a deferred tool with `tool_search` still costs one cache miss.
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
|
|
77
|
+
- **Feishu topic titles have a budget, and a work request no longer stays in the group.** The group prompt and the `feishu_start_topic` description ask for a one-line title of at most 40 characters that names the task, with requirements, method and status left out, and say that any request needing work must become a topic while only short questions are answered in place. The brief keeps the requirements. This is a prompt change: there is no length check or truncation in code.
|
|
78
|
+
- **A provider content filter fails the run instead of ending it silently.** When a model response stops with AI SDK finish reason `content-filter`, the assistant message ends with `stopReason: 'error'` and `errorMessage: "Provider stopped the response: content_filter"`, the run ends with `agent_end` `reason: 'error'`, and `prompt()` rejects with that reason, so the TUI, `-p` / `--mode json` and channels show it. Text streamed before the filter stays in the context; the response is not retried.
|
|
79
|
+
- **A Feishu group no longer repeats a mention already handed to a topic.** After Vela opens a topic for a top-level message, or forwards that message into a topic, later `[Chat since last reply]` scans stop at that trigger's create time. Messages other people send after the trigger still appear. Vela remembers the stop for this process. After a restart the scan falls back to an own-app message in the group list.
|
|
80
|
+
- **Switching models no longer replays the previous model's reasoning.** An assistant message records the model that produced it (`producedBy`, `provider/modelId` as the AI SDK names the model, so the same model chosen by name or as an object has one identity), and a request leaves out reasoning another model wrote — its signature or encrypted content is only valid for the model that wrote it, and the new model could carry it into the visible answer. Text and tool calls stay, the history keeps everything, and switching back sends the earlier model its own reasoning again, also after a resume.
|
|
81
|
+
- **A resumed session declares the deferred tools it loaded.** `resume()` reads the session's `tool_search` results and declares the tools they loaded again, so the model goes on using them without searching a second time. Sessions without such results are unchanged.
|
|
82
|
+
- Routine lists show a readable topic task without its opaque root message id; routine ids remain available for cancellation.
|
|
83
|
+
|
|
84
|
+
### Breaking Changes
|
|
85
|
+
|
|
86
|
+
- **The built-in bash gate is gone, and so is `security_warning`.** Vela no longer rejects bash commands that match its old pattern list: a command runs unless a session `permissions` rule, a hook or a `tool_call` extension blocks it. `src/security/bash-classifier.ts` and the `security_warning` event are removed; listeners should drop that branch. For a confirmation prompt on a smaller pattern list, enable the new opt-in `permission-gate` extension. Commands the old gate rejected for no good reason (a harmless recursive force-delete under a temporary directory, a `grep` whose pattern contains such a command) now run. See [Security](docs/security.md).
|
|
87
|
+
|
|
88
|
+
- **Session roles are gone (like pi).** `Role`, `ChannelDefinition.roleFor`, `session.role` / `vela.session(id, { role })`, the `/role` command and the `Role` line of `/status` are removed. Every session has the same tools, prompt and commands, and an enabled chat is trusted as a whole. Restrict a session with `permissions` (`{ bash: 'ask', write_file: 'deny' }`) and `tools` instead; a tool without a rule is allowed. Guest and collaborator sessions, and their prompt differences, no longer exist. See [Security](docs/security.md#per-session-permissions).
|
|
89
|
+
|
|
90
|
+
## [0.3.0] - 2026-10-19
|
|
91
|
+
|
|
92
|
+
### Added
|
|
93
|
+
|
|
94
|
+
- **Feishu scheduled wake-ups and management.** `feishu_routines` creates, lists and cancels persisted one-off or daily/selected-weekday routines in the deployment timezone for the current logical topic, group or DM. `!routines` lists the chat's schedules and next runs without a model call, even with `readUntagged: false`. Wake-ups survive restarts, queue behind active work and mention their creator; startup catches up the latest missed run once with a late notice, and cancellation only prevents future submissions.
|
|
95
|
+
|
|
96
|
+
- **Feishu evaluates untagged human group discussion by default.** Set `readUntagged: false` per group for mention-only top-level intake. Each unique supported message is queued; `feishu_skip_reply` keeps unnecessary answers silent. Bot/disabled-chat events have no content side effects; passive work is not resumed after restart. Silence still incurs model cost, with no spend cap. Group history now stops at any own-app message, including cards after restart.
|
|
97
|
+
|
|
98
|
+
- **Feishu group overview and bounded history search.** Topic lists include requester, activity and durable waiting/done/stopped state. `feishu_wait_for_input` marks a required decision until the next accepted topic input. `feishu_search_history` searches only the current channel/topic's group, with explicit scan/result limits and topic-root coverage. Custom Feishu transports now implement `searchMessages`; experimental topic state replaces `idle` with `done`, `waiting` or `stopped`.
|
|
99
|
+
|
|
100
|
+
- **Feishu groups have their own workspace, memory and tool selection.** Group topics share these resources across idle reopen, process restart and `!restart`; existing attachments stay in place. Groups read/search shared global notes but write only their own memory through the memory API. Object-form chat configuration validates exact tool names before connecting, with channel controls retained. Cwd is not a security sandbox.
|
|
101
|
+
|
|
102
|
+
- **SDK sessions can choose their working directory.** `vela.session(id, { cwd })` scopes file/search/bash tools and automatic project instructions to that directory; `session.cwd` exposes it. Application resources and export destinations stay Vela-wide. Fork/clone preserves cwd; callers pass it again on reopen/resume. This is not a security sandbox.
|
|
103
|
+
- **Feishu topic `!status`, with or without a mention.** Reports the model, estimated context and durable topic/group token and known-cost totals without interrupting the run or calling the model. The append-only channel usage ledger includes compaction and keeps totals across session generations; coverage and unknown prices are explicit.
|
|
104
|
+
- **Feishu topic `!stop` and `!restart`, with or without a mention.** Stop retains the session; Restart archives it and reloads topic history on the next message. A failed index write retains the previous active generation even when another message is admitted concurrently.
|
|
105
|
+
- **Channel restart recovery and durable topic listing.** The gateway resumes saved unanswered inputs with an interruption notice, delivers already-saved final answers and asks for unsaved admitted group messages to be resent. One atomic directory under `dataDir/channels/index.json` preserves owned topics across restarts; stopped runs remain stopped. Existing topics join on their next message.
|
|
106
|
+
|
|
107
|
+
### Fixed
|
|
108
|
+
|
|
109
|
+
- Feishu routine management no longer fails on Responses-compatible providers that require an object-root tool schema; operation-specific validation and chat scoping are preserved.
|
|
110
|
+
|
|
111
|
+
- **An unconfigured knowledge base printed a notice on every CLI start.** Startup is now quiet when embedding is not configured; `/rag` explains the missing configuration and `/extensions` lists the command. Configured knowledge-base warnings and errors are unchanged.
|
|
112
|
+
- DeepSeek Responses plaintext thinking (`response.reasoning_text.delta`) is now displayed and saved for `api.deepseek.com`; the SDK previously dropped these deltas. Reasoning replay remains unsupported.
|
|
113
|
+
- Feishu topic prompts now ask the model to respond to the latest message and treat earlier results as delivered, without repeating them unless asked; completion notices call for a brief acknowledgment without restating the task or result.
|
|
114
|
+
- DeepSeek Responses requests use `store: false` before SDK serialization so follow-ups receive earlier assistant answers as text instead of unsupported server-side item references. Local history and other providers are unchanged; reasoning-event parsing and replay support are outside this fix.
|
|
115
|
+
|
|
116
|
+
### Breaking Changes
|
|
117
|
+
|
|
118
|
+
- **Feishu top-level intake defaults to on.** Use `{ id, readUntagged: false }` to retain mention-only behavior. Custom transports must include the now-required `sender.sender_type: "user"` for human events. Custom transports without `appId` keep their current-process reply-receipt boundary; supply `appId` for own-app history boundaries after restart. Before downgrading, stop Vela, remove object-shaped passive entries from `pendingMessageIds` and mark active passive records done; older directory readers accept only string IDs.
|
|
119
|
+
|
|
120
|
+
- **Channel feedback uses one experimental lifecycle.** Replace `acknowledge`, `showTopicControls` and `showRunControls` with `startRun(message, { acknowledge })`, returning `event(update)` and `finish(outcome)`. Move controls and agent progress into the corresponding updates, and combine terminal card/reaction handling in `finish`; channel hooks remain experimental because Feishu is the only channel implementation so far, with stabilization once a second channel is implemented. See [migration guidance](docs/channels.md#migrating-existing-feedback-hooks). Feishu behavior and session data are preserved.
|
|
121
|
+
|
|
122
|
+
- **Feishu topic runs now show live feedback.** Group inputs receive `OnIt` then `DONE`/`CrossMark`; one shared Stop card shows bounded tool progress and a 15-second heartbeat. Topic results and decision questions @-mention the original requester. Custom `FeishuTransport` implementations must return a message ID from `sendCard` and implement `patchCard`, `addReaction` and `removeReaction`. See [task feedback and permissions](docs/channels.md#task-feedback).
|
|
123
|
+
|
|
124
|
+
- **Feishu group tasks can now open topics.** The channel prompt distinguishes short direct answers from work handed off through `feishu_start_topic`; task execution does not block the channel. Original messages can be forwarded into same-group owned topics, and dispatch tools are hidden from topic sessions and DMs. Custom transports must implement `startTopic(messageId, task)` with a root/thread receipt and `forward(messageId, threadId)` with a message receipt. Existing send permission covers both; see [channel dispatch](docs/channels.md#channel-dispatch).
|
|
125
|
+
|
|
126
|
+
- **Feishu topics now have separate sessions.** Mentioning Vela inside a topic establishes its conversation; later replies from any member need no @ and automatically steer a running task. Each topic run has a Stop card that preserves context. Grant `im:message.group_msg` to receive untagged replies. The first mention in an unowned topic reads its full paginated history through the trigger timestamp; later mentions reuse stored context; top-level group messages and DM controls retain their existing behavior. Custom `FeishuTransport` implementations must add `listTopicMessages(threadId, pageToken?)`, returning ascending `{ items, pageToken? }` pages. Existing shared group history is not migrated into individual topics.
|
|
127
|
+
|
|
128
|
+
- **Custom Feishu transports must support run cards.** Implement `sendCard(chatId, card, replyToId?)` for Card 2.0 payloads and `start(onMessage, onAction)` with the v2 `card.action.trigger` callback and its toast response. Enable that callback subscription in Feishu's long-connection settings; existing message-send permissions cover the cards. See [Channels](docs/channels.md#run-controls).
|
|
129
|
+
- **History is Vela's `ModelMessage`, and an inline image is `{ type: 'image', data, mimeType }`.** `session.messages`, the `context` hook's `messages`, `message_end` events, RPC `get_messages`, and session `message` entries use this type. The image part stores base64 in `data` and a media type in `mimeType`. The AI SDK image part `{ type: 'image', image, mediaType }` is deprecated in ai@7. History that contains an image is not an AI SDK `ModelMessage`. `session.messages` is not assignable to the AI SDK `ModelMessage[]` type. Text-only history still works if you pass it to the AI SDK at runtime. To migrate, import `ModelMessage` from `@glows777/vela` for history and for a `context` handler. Map each stored image part to the file part `{ type: 'file', data, mediaType }` before you pass image history to `generateText` or another AI SDK call. Vela does that mapping on the provider request and leaves the saved image in place. See [Image input](docs/sdk.md#image-input) and [Messages](docs/session-format.md#messages).
|
|
130
|
+
- **Channels now share one owner session per enabled chat.** Feishu `chats` lists enabled chat_ids; `owners` now enables only those open_ids' direct chats. A deployment with only `owners` no longer answers in groups until each trusted group is listed in `chats`. Every member of an enabled chat has owner access to bash, files, memory and extensions. Groups run only on an @mention of this bot, include up to 50 recent history messages with cached display names, and reply to the triggering message. Empty `chats` logs a startup notice; unlisted chats are ignored and logged. Session ids change from `<channel>-<chat>-<sender>` to `<channel>-<chat>`; old per-sender files are kept but not combined or automatically resumed. Core SDK roles are unchanged. Custom channels default to owner and must enforce enablement before delivery. Custom `FeishuTransport` implementations need bot identity, contact lookup, history and reply methods. See [channel migration and required Feishu permissions](docs/channels.md#migration).
|
|
131
|
+
|
|
132
|
+
### Fixed
|
|
133
|
+
|
|
134
|
+
- **Responses thinking controls were dropped for unfamiliar reasoning model ids.** Configured/catalog `reasoning: true` now enables SDK serialization and existing `thinkingLevelMap` strings reach `reasoning.effort`, preserving explicit provider options. Responses requests to `api.deepseek.com` keep system instructions in the system role. See [thinking levels and SDK limitations](docs/models.md#thinking-levels).
|
|
135
|
+
|
|
136
|
+
- **Headless session save failures were silent.** Channels now log the error with the session id and refuse new prompts after three consecutive failures until a successful write resets the count. Print mode (`-p`) reports save failures on stderr while keeping the answer on stdout.
|
|
137
|
+
|
|
138
|
+
- **A turn stopped at `summaryThreshold` when there was nothing to summarize.** A message above that threshold and below `maxInputTokens` is sent. Only a request over `maxInputTokens` stops the turn.
|
|
139
|
+
- **The loop-detection warning was broken English, stored as a user message, and fired one call late.** It now fires on the 10th identical call (a critical stop on the 20th). The sentence names that count. The session stores it as a custom message (`customType: loop`), so a resumed session does not show it as something you typed. The model still sees the text.
|
|
140
|
+
- **`--no-session` still wrote session files.** The transcript stayed in memory, and `/status` said `in-memory (not saved)`, but a tool turn still appended `tool-history.jsonl` and `usage/today.jsonl` under the data directory. Those, and long tool output, now stay out of the data directory. Tool history and long output use a temporary directory removed when the session closes. Memory and the knowledge base are unchanged. Usage totals stay available in `/status` and `/usage`.
|
|
141
|
+
- **The CLI never showed which `AGENTS.md` / `CLAUDE.md` files it loaded.** Print, JSON and RPC mode now print one `[context]` line on stderr. Interactive mode prints that line in the chat log at startup, and `/status` lists the same paths after `Context files:`. A file in the working folder is relative to it. A file under the home directory uses `~`, and a file under the agent directory outside the home directory uses `$VELA_DIR`. `--no-context-files` prints nothing.
|
|
142
|
+
- **The offline demo model (`VELA_MODEL=mock`) crashed Vela when you pressed Esc** while it was answering (`Controller is already closed`, exit 1). Its stream is now pull-based, so an interrupted answer just stops. `test parallel` and `test loop` called a `get_weather` tool that no longer exists and now use built-in tools, and any prompt containing "ls" inside a word ("tools", "details") no longer lists files.
|
|
143
|
+
- **`/status` disagreed with the footer and named a session file that did not exist yet.** The dollar amount now uses the same four-decimal rounding as the footer and `/usage` (`$0.0016`, not `$0.002` in one place and `$0.0016` in the other). A session that has not been saved says `not written yet (will save to …)` instead of a missing `.jsonl` path. Long paths shorten `~` under the home directory and `$VELA_DIR` only when the file is under the configured agent directory. The Path line is fitted to the terminal's display columns, so a session id is not broken across lines at 80 or 120 columns, and an unsaved session keeps `not written yet` even when the destination does not fit.
|
|
144
|
+
- **Shutdown failures no longer skip independent cleanup.** `vela.dispose()` and `session.close()` now reject with an `AggregateError` after their remaining cleanup attempts settle, including failures from shutdown hooks that were previously only logged. Callers should handle the rejection; repeated calls return the same result without retrying cleanup.
|
|
145
|
+
- **`vela.dispose()` now waits for asynchronous extension factories to settle** before tearing down their resources. A factory that never settles can therefore keep disposal pending indefinitely; no timeout or forced termination is implied.
|
|
146
|
+
- **The `memory` tool's `read` and `delete` accepted paths.** A file name such as `../../x` reached files outside the memory directory: `read` rewrote the file it read (adding a `lastReadAt` line and setting mode `0600`) and `delete` removed it, even in sessions whose permissions denied `bash` and file writes. Both now accept only a memory file name from the index.
|
|
147
|
+
- **Channel sessions were never closed**, so a long-running bot kept one session per conversation and sender in memory for as long as it ran. An idle channel session is now closed after 10 minutes (`createVela({ channelIdleMs })`) and resumed on the next message.
|
|
148
|
+
- **Guest system prompts included the tool call history guide**, with absolute paths of the data directory (which reveal the home directory and the project path) and instructions for tools a guest doesn't have. Guests no longer get it.
|
|
149
|
+
- **Print, JSON and RPC modes left commands running when stopped by a signal.** `bash` runs commands in their own process group, which outlived a `vela -p` killed with SIGTERM or Ctrl+C. These modes now abort the run, kill the commands, save the session and exit with 130 (SIGINT), 143 (SIGTERM) or 129 (SIGHUP); a second signal exits right away.
|
|
150
|
+
- **Feishu**: a message delivered twice is handled once; rich-text (`post`) messages are read as text instead of being dropped; images, files and other messages get a reply asking for text.
|
|
151
|
+
- **A second `vela.dispose()` or `session.close()` returned before the first one finished**, so a caller could exit before extensions' shutdown handlers and the final save ran. It now waits for the close in progress.
|
|
152
|
+
- **The knowledge base's embedding cache had no limit**: every ingested chunk's text and vector stayed in memory. It now keeps the 1000 most recently used texts per embedder.
|
|
153
|
+
- **`web_fetch` read the whole response into memory.** It now reads at most 5 MB and says when the page was cut.
|
|
154
|
+
|
|
155
|
+
### Added
|
|
156
|
+
|
|
157
|
+
- **Feishu Queue / Steer / Stop cards during a chat run.** Queue remains the default with one answer per message. Steer joins the active task for one combined reply; Stop aborts with a visible confirmation and cancels that run's pending steers while keeping other gateway messages queued. Choices are bound to the enabled chat and original run; repeated or expired callbacks cannot control later runs.
|
|
158
|
+
- **Images in SDK prompts and tool results.** Core prompts, extension hooks, RPC, `read_file` and MCP share inline image content with lazy Photon WASM normalization (a new runtime dependency). Native tool images work with Anthropic/Responses; Chat-compatible providers receive request-only user attachments. CLI/TUI attachment entry is planned separately.
|
|
159
|
+
|
|
160
|
+
- **Core image input.** `PromptOptions.images` attaches images to `prompt`, `steer`, and `followUp`. `sendUserMessage` accepts a string or an array of text and image parts. `InputEvent.images` exposes the same parts to the `input` hook. RPC `prompt`, `steer`, and `follow_up` accept an `images` array. The package exports `ImageContent`, `ModelMessage`, `UserContent`, and `TextContent`. An image is `{ type: 'image', data, mimeType }`. Saved history keeps the normalized image. A text-only model receives a placeholder in that request only. See [Image input](docs/sdk.md#image-input).
|
|
161
|
+
- `/status` in interactive mode: a session overview with the id, file path (not written yet until the first save; in-memory when the session is not saved; custom storage when the location is not reported), working directory, model, thinking level, role, message count, context estimate and token usage. `session.persistence` is `{ type: 'file', path }`, `{ type: 'memory' }`, or `{ type: 'unknown' }` when a custom storage does not say. The dollar amount matches the footer.
|
|
162
|
+
- `createVela({ channelIdleMs })`, and `channelIdleMs` in `createTestVela()`.
|
|
163
|
+
- `feishu({ transport })` and the `FeishuTransport` / `FeishuMessageEvent` types, so the Feishu channel can be tested without a Feishu app.
|
|
164
|
+
|
|
165
|
+
## [0.2.0] - 2026-10-11
|
|
166
|
+
|
|
167
|
+
### Breaking Changes
|
|
168
|
+
|
|
169
|
+
- **Events now have pi's shape.** `message` is replaced by `message_start` / `message_end`; `text_delta` and `thinking_delta` by `message_update` (`assistantMessageEvent.type` is `text_delta` / `thinking_delta` and the text is in `delta`, alongside `text_start` / `text_end`, `thinking_start` / `thinking_end` and `toolcall_start` / `toolcall_delta` / `toolcall_end`); `tool_call` by `tool_execution_start` (`input` is now `args`); `tool_result` and `tool_error` by `tool_execution_end` (`output` / `error` is now `result`, with `isError` and `durationMs`); `retry` by `auto_retry_start` (`maxRetries` → `maxAttempts`, `error` → `errorMessage`) and the new `auto_retry_end`. `turn_end` has `message` and `toolResults` instead of `needsToolCall`; `agent_end` adds `messages`. The assistant `message_end` carries `stopReason` (`stop`, `toolUse`, `length`, `aborted`, `error`) and `errorMessage`. `--mode json`, `--mode rpc` and SDK subscribers see the new events. To migrate, print `message_update` events whose `assistantMessageEvent.type` is `text_delta`, and read final messages from `message_end`. See [SDK events](docs/sdk.md#events) and [JSON mode](docs/json.md). The extension `tool_call` / `tool_result` interception events are unchanged.
|
|
170
|
+
- **`bash` has no default timeout** (same as pi). The model passes an optional `timeout` in seconds instead. `limits.bashTimeoutMs` is removed; setting it in `settings.json` or `createVela({ limits })` is now an "unknown key" error. Remove the key.
|
|
171
|
+
- **`edit_file` takes pi's parameters**: `{ path, edits: [{ oldText, newText }] }` instead of `old_string` / `new_string`. A text that is not found, found more than once or overlapping another edit is now a tool error instead of a successful result describing the problem. Faux scenarios that call `edit_file` need the new arguments.
|
|
172
|
+
- **`ToolDefinition.isConcurrencySafe` is replaced by `executionMode`** (`'parallel'`, the default, or `'sequential'`, same as pi). Drop `isConcurrencySafe: true`; replace `isConcurrencySafe: false` with `executionMode: 'sequential'` if the tool must not run alongside the session's other calls. Registering a tool that still sets `isConcurrencySafe` throws, so the change is not silently ignored.
|
|
173
|
+
- **Skills use the Agent Skills format and the model loads them itself** (like pi). The system prompt now lists each skill's name, description and `SKILL.md` path, and the model reads the file with `read_file` when a task matches. `description` is required (skills without one are skipped with a warning) and the name comes from the `name` front matter, falling back to the directory name. `when_to_use` is no longer read: fold it into `description`. `/skill load`, `/skill unload`, `/<skill-name>` and the per-session active state are gone: send a skill yourself with `/skill:<name> [instruction]`, which now works in every mode and in `session.prompt()`. When two skills share a name, the first one found wins (project before user) instead of the last. See [Skills](docs/settings.md#skills).
|
|
174
|
+
- `FixtureSkill` in `@glows777/vela/testing` drops `whenToUse` and gains `disableModelInvocation`.
|
|
175
|
+
- **`ToolDefinition.isReadOnly` is replaced by `annotations`** (pi's and MCP's `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`). Replace `isReadOnly: true` with `annotations: { readOnlyHint: true }`. Registering a tool that still sets `isReadOnly` throws.
|
|
176
|
+
- **A failed `bash` command is an error result** (same as pi): a non-zero exit, a timeout or an abort ends with `tool_execution_end` `isError: true`, and the model gets the output preview and status as the error text. Tools returning a `ToolExecutionResult` with `isError` are error results too. Code that read the status line from a successful result should check `isError`.
|
|
177
|
+
- `tool_search` is `model-only`: other tools cannot run it with `ctx.executeTool()`.
|
|
178
|
+
- **Sessions are an append-only log in pi's format (session file version 2).** Each message is appended to the session as it enters the history instead of the whole history being saved when `prompt()` ends, so a crash loses at most the step in progress. Like pi, a summary appends a `compaction` entry and folded tool output a `context_edit` entry; the original messages stay in the file. Model, thinking level and name are entries too. Version 1 files are converted when loaded and rewritten in the new format. See [Session format](docs/session-format.md).
|
|
179
|
+
- **`SessionStorage` has `load` / `append` instead of `load` / `save`**, and `SessionCheckpoint` is removed. `load(id)` returns the header and entries (`SessionFileEntry[]`), `append(id, entries)` adds entries in order. Custom storages store entries as given; `summarizeSession(id, entries)` builds a `list()` row and `migrateSessionV1(id, lines)` converts data saved by the old checkpoint storage. See [Session storage](docs/sdk.md#session-storage).
|
|
180
|
+
- **Summaries emit pi's `compaction_start` / `compaction_end`** (`reason`: `threshold`, `manual` or `overflow`; `compaction_end` has `result`, `aborted`, `willRetry`, `errorMessage`) instead of the `context` event's `summary`, `compact` and `overflow` actions; `context` remains for microcompaction (`micro`) and `summary-required`. The RPC `compact` command returns the compaction result.
|
|
181
|
+
- **The `context` event is renamed `context_prepare`** (same fields: `action` `micro` or `summary-required`, token counts). `context` is now pi's extension hook that changes the messages of each request. Subscribers, `--mode json` and `--mode rpc` clients that read `context` should read `context_prepare`.
|
|
182
|
+
- **An assistant message cut off while it streamed (abort or failure) is no longer sent to the model**, like pi: it is appended to the session with `stopReason: 'aborted'` or `'error'` but left out of `session.messages`. A message the model finished, whose tools were running, still stays in the history with its tool calls answered.
|
|
183
|
+
- **`tool_search` searches by keyword and loads tools instead of returning schemas** (like pi). It takes `query` (keywords, or exact tool names separated by commas) and an optional `limit` (default 8), ranks deferred tools with BM25 and loads the best matches for the model's next call; the result lists the loaded tools and each namespace's `instructions` once. The system prompt names each namespace (such as an MCP server) once with its tool count instead of listing its tools. Faux scenarios that read the old JSON result of `tool_search` need updating. See [Tools](docs/tools.md#deferred-tools-and-tool_search).
|
|
184
|
+
- An extension tool whose name already starts with `<extension name>_` is no longer prefixed again (`web_x` from the `web` extension stays `web_x`, not `web_web_x`).
|
|
185
|
+
- **Prompting a saved session that was not resumed fails** with `Session <id> already has saved history: call resume() to continue it, or use another session id`. Before, the old history was silently overwritten at the end of the prompt. Call `await session.resume()` first, or use a new id.
|
|
186
|
+
- **The built-in `openai` provider uses the Responses API** (`openai-responses`, like pi) instead of Chat Completions. For an OpenAI-compatible service behind `OPENAI_API_BASE_URL` that only speaks Chat Completions, set `"api": "openai-completions"` on `openai` in `~/.vela/models.json`, or add the service as its own provider.
|
|
187
|
+
- **An unsupported thinking level is clamped instead of failing** (like pi): a model with `reasoning: false` runs with `off`, and `xhigh` / `max` fall back to `high` unless the model's catalog entry supports them. `max` is now sent to the AI SDK as `max` (it was `xhigh`), and `off` sends `reasoning: 'none'` to models without `reasoning: false`. `/thinking`, Shift+Tab and RPC `get_available_thinking_levels` offer only the levels the model supports. See [Thinking levels](docs/models.md#thinking-levels).
|
|
188
|
+
- **`vela.models()`, `/model`, `--list-models` and RPC `get_available_models` list only available models**: those of providers that have a credential, like pi. `vela.models({ all: true })` returns every listed model.
|
|
189
|
+
- **`ProviderDefinition.createModel(id, ctx)`** gets a second argument with `ctx.credential()`; providers that ignore it keep working. `ModelRegistry` takes a `CredentialStore` as its second argument.
|
|
190
|
+
- **Without `--model`, `defaultModel` or `OPENAI_API_MODEL_NAME`, Vela picks pi's default model** of the first built-in provider that has a credential (Anthropic, then OpenAI, then OpenRouter), and `createVela()` without `model` does the same, instead of reporting that no model is selected. The picked model is saved with the session (like pi), so resuming it keeps that model.
|
|
191
|
+
- The built-in price table (`PRICE_TABLE`) is gone: prices come from the model catalog or `cost` in `models.json`.
|
|
192
|
+
- **`--list-models` exits with code 1 when an extension failed to load.** The models that did load are still listed and the load errors go to stderr. pi exits 0 here; Vela fails loudly so scripts notice an incomplete list.
|
|
193
|
+
|
|
194
|
+
### Fixed
|
|
195
|
+
|
|
196
|
+
- A response cut off by the output token limit while calling tools no longer breaks the session (every later prompt failed with `AI_InvalidPromptError`): like pi, each truncated call gets an error result asking the model to re-issue it. See [Sessions](docs/sessions.md#truncated-responses).
|
|
197
|
+
- An aborted or failed turn keeps what it produced: tool calls that were running stay in the history, each paired with its result or `Operation aborted`, and a message cut off while streaming is kept in the session. Before, the whole step was dropped and the model didn't know a tool had already run. See [Sessions](docs/sessions.md#interrupted-and-failed-turns).
|
|
198
|
+
- A request that already started running tools is no longer sent again after an error, so tools don't run twice.
|
|
199
|
+
- Interactive mode no longer stays running and spins a CPU after its terminal closes: on SIGHUP it saves and exits even though restoring the terminal fails, and a read or write error on a dead terminal exits at once with code 129, like pi.
|
|
200
|
+
- When a request is retried, the interactive mode keeps the failed attempt's text marked `(response failed)` and shows the retry as a new message, instead of appending the retry to the old text.
|
|
201
|
+
- **The knowledge base no longer mixes vectors from different embedding models.** It records the embedder that built it, and when the configured one differs, ingesting and searching fail with an error naming both instead of returning meaningless scores. Switch back, or run `/rag clear` and ingest again. A knowledge base from 0.1.0 has no record: the configured embedder is assumed, recorded and a warning logged. See [rag](docs/built-in-extensions.md#rag).
|
|
202
|
+
|
|
203
|
+
### New Features
|
|
204
|
+
|
|
205
|
+
- **MCP servers** (built-in `mcp` extension, like pi's): configure servers in `~/.vela/mcp.json` or the trusted project's `.vela/mcp.json` (the `mcpServers` format of Claude Desktop and Claude Code), over stdio or streamable HTTP. Their tools are `mcp__<server>__<tool>`, deferred by default and loaded with `tool_search`; `exposure` / `toolExposure` make them `direct` or `hidden`. One set of connections serves all sessions of a Vela. `/mcp` shows server status and `/mcp reconnect <server>` reconnects. Guests cannot use MCP tools. SDK apps add `mcp({ servers })`. Not yet supported: OAuth sign-in, MCP resources and codemode. Uses the official `@modelcontextprotocol/sdk`. See [MCP servers](docs/mcp.md).
|
|
206
|
+
- Extension API: `unregisterTool(name)`, `onShutdown(handler)` (runs on `vela.dispose()`), and `registerTool()` returns the registered name. Tools can be registered at any time.
|
|
207
|
+
- `loadConfig()` returns `mcp` with the servers from `mcp.json`; `.vela/mcp.json` counts as project config that needs trust.
|
|
208
|
+
- **Context overflow recovery**: when the provider says the context is too long, Vela summarizes the history once and sends the step again, like pi (`compaction_end` with `reason: 'overflow'`). See [Sessions](docs/sessions.md#context-overflow).
|
|
209
|
+
- `session.getEntries()` returns the session's entries, including summarized messages and interrupted assistant messages.
|
|
210
|
+
- Calls a tool makes through `ctx.executeTool()` are recorded on its tool result in the session (`nestedCalls`, like pi), not sent to the model.
|
|
211
|
+
- **Anthropic prompt caching**: the system prompt, last tool and last message are sent as cache breakpoints, like pi. See [Sessions](docs/sessions.md#prompt-caching).
|
|
212
|
+
- **Automated releases**: `bun run release <patch|minor|x.y.z>` tags a release, and CI publishes it to npm with provenance and creates the GitHub release from this changelog.
|
|
213
|
+
- `edit_file` applies several disjoint edits in one call, falls back to fuzzy matching (trailing whitespace, smart quotes, Unicode dashes and spaces), keeps BOM and CRLF line endings, and records a diff and unified patch in the tool history. Ported from pi.
|
|
214
|
+
- `shellPath` setting and `createVela({ shellPath })` choose the shell of the `bash` tool.
|
|
215
|
+
- `withFileMutationQueue(path, fn)` is exported for extension tools that write files.
|
|
216
|
+
- **AGENTS.md / CLAUDE.md**: project instructions go into the system prompt, found like pi: `~/.vela/AGENTS.md`, then one file per directory from the filesystem root down to the working folder (`AGENTS.override.md`, `AGENTS.md`, `CLAUDE.md`, ...). Like pi they load whether or not the project is trusted; guest sessions don't get them. `--no-context-files` turns them off; the SDK takes `contextFiles` and exports `loadContextFiles()`. See [Context files](docs/settings.md#context-files).
|
|
217
|
+
- **Prompt templates**: `/<name> [args]` expands a Markdown template from `~/.vela/prompts/`, `.vela/prompts/` (trusted projects) or the new `prompts` setting, with pi's `$1`, `$@`, `${N:-default}` and `${@:N}` placeholders. Templates and skills show up in `/` completion and in RPC `get_commands` (with pi's `source` field). See [Prompt templates](docs/settings.md#prompt-templates).
|
|
218
|
+
- **More skill locations and options**: `.agents/skills/` in the project (up to the git root, trusted projects) and `~/.agents/skills/`; nested skill directories; `disable-model-invocation: true` for skills only you can run.
|
|
219
|
+
- **Appending to the system prompt**: `--append-system-prompt <text or file>` (repeatable), `~/.vela/APPEND_SYSTEM.md` and `.vela/APPEND_SYSTEM.md` (trusted projects), and the SDK's `appendSystemPrompt`. See [Settings](docs/settings.md#appending-to-the-system-prompt).
|
|
220
|
+
- System prompt sections follow pi's order (rules, addendum, project context, skills, working directory, then extension sections), and the skills list no longer changes during a session, which keeps the prompt cache prefix stable.
|
|
221
|
+
- **`ctx.executeTool(name, args, { signal, onUpdate })`** lets a tool run another tool through the same permissions, hooks, `bash` check, confirmation and tool history as a model call, same as pi. Nested calls get ids `<parent>/<n>` and `parentToolCallId` on their events, and failures come back as `isError` results instead of throwing. See [Extensions](docs/extensions.md#registertool).
|
|
222
|
+
- **Streaming tool output**: `ctx.onUpdate(partialResult)` emits `tool_execution_update` (pi's event), and `bash` sends the tail of its output while it runs.
|
|
223
|
+
- **Session tree** (like pi): `/tree` moves to an earlier point of the session without losing the branch you leave (pick one of your messages to edit and resend it), optionally summarizing that branch (`branch_summary` entry) or labelling an entry (`label` entry). `/fork` continues in a new session from before an earlier message and `/clone` copies the current branch; the new session records `parentSession`, and `--fork <id>` does the same from the command line. The SDK has `navigateTree()`, `fork()`, `clone()`, `setLabel()`, `getTree()`, `getBranch()` and `getLeafId()`; unlike pi, `fork()` and `clone()` open the copy as another session and leave the original open. RPC gains pi's `get_tree`, `get_entries`, `get_fork_messages`, `fork` and `clone`, and extensions get pi's `session_before_tree`, `session_tree` and `session_before_fork` events. The file format stays version 2. See [Sessions](docs/sessions.md#session-tree).
|
|
224
|
+
- **Export**: `/export [path]`, `session.exportHtml()` / `exportJsonl()`, RPC `export_html` and `vela --export <id or file> [output]` write the current branch as a self-contained HTML page (or as JSONL). See [Sessions](docs/sessions.md#export).
|
|
225
|
+
- **Automatic compaction can be turned off** (pi's `compaction.enabled`): the `autoCompaction` setting, `createVela({ autoCompaction })`, `vela.session(id, { autoCompaction })`, `session.autoCompaction` and RPC `set_auto_compaction`. Off means no microcompaction, threshold summary or compact-and-retry on overflow; `/compact` still works. See [Sessions](docs/sessions.md#turning-automatic-compaction-off).
|
|
226
|
+
- **Extension messages, like pi**: `session.sendMessage({ customType, content, display, details }, { triggerTurn, deliverAs })` adds a custom message the model sees as a user message (`steer`, `followUp` and `nextTurn` delivery as in pi), `session.sendUserMessage(text)` sends input as if typed, and `session.appendEntry(customType, data)` saves extension state. They are on the session because one Vela serves many sessions. Custom messages are saved as `custom_message` entries, carry `custom` on their `message_start` / `message_end` events, and show in the TUI as a `[customType]` block when `display` is true. `before_agent_start` handlers can return `{ message }`. See [Extensions](docs/extensions.md#messages).
|
|
227
|
+
- **New extension events from pi**: `input` (rewrite or handle user input; `PromptOptions.source` says where it came from), `context` (change the messages of each request without touching the history), `before_provider_request` (change the AI SDK call options, including headers), `after_provider_response` (response headers) and `provider_stream_event` (raw provider chunks). `tool_call` and `tool_result` carry `parentToolCallId` for calls made through `ctx.executeTool()`. See [Extensions](docs/extensions.md#intercepting-events).
|
|
228
|
+
- **Tool metadata from pi**: `exposure` adds `model-only`, `codemode` and `hidden` to `direct` and `deferred`; `namespace` groups deferred tools (and is returned by `tool_search` with its `instructions`); `annotations` describe what a tool does. See [Tools](docs/tools.md#deferred-tools-and-tool_search).
|
|
229
|
+
- **Interactive mode tool blocks like pi's**: `bash` output streams into its block while it runs, with the elapsed time, and the collapsed block keeps the last 5 lines; `edit_file` shows a colored diff with changed words highlighted; other tools show the first 10 lines; calls made through `ctx.executeTool()` are listed inside the calling tool's block. See [CLI](docs/cli.md#tool-output).
|
|
230
|
+
- `tool_execution_end` has `details`: display data the model doesn't get, such as `edit_file`'s diff (pi's `result.details`). Extension tools set it by returning `new ToolExecutionResult(details, text)` (now exported) instead of a string.
|
|
231
|
+
- **`/copy`** copies the last answer to the clipboard, like pi (system clipboard tools, or OSC 52 over SSH).
|
|
232
|
+
- **`!command` and `!!command`** in interactive mode run a shell command yourself, like pi: `!` sends the command and its output to the model with your next message, `!!` keeps it out; Esc cancels. The SDK has `session.executeBash()` / `abortBash()` and RPC the `bash` / `abort_bash` commands; results are saved as `bash_execution` session entries, and output streams as `bash_execution_update` events. See [CLI](docs/cli.md#interactive-mode).
|
|
233
|
+
- A resumed session in interactive mode shows tool blocks as they were, with `edit_file` diffs and nested calls: tool messages in the session keep each tool's `details`.
|
|
234
|
+
- **`--tools` / `--no-tools` / `--exclude-tools`** (`-t` / `-nt` / `-xt`) choose the tools by name or `*` pattern, like pi; an entry that matches no tool is an error. **`--list-models [search]`** lists the configured models.
|
|
235
|
+
- **Built-in model catalog** generated from pi's (`@earendil-works/pi-ai`, MIT): the built-in providers list their models, and any known model id, built-in or from `models.json`, gets its context window, output limit, price, image input and thinking levels. `bun scripts/update-models.ts` regenerates it. See [Models](docs/models.md#the-model-catalog).
|
|
236
|
+
- **`openrouter` is a built-in provider** (`OPENROUTER_API_KEY`), with reasoning sent as OpenRouter's `reasoning: { effort }`.
|
|
237
|
+
- **`/login` and `/logout`** (like pi): store an API key, or sign in with **Sign in with ChatGPT** (use a ChatGPT Plus/Pro plan in the local terminal, through OpenAI's token sharing for open-source agents) or **OpenRouter** (creates a key). Credentials live in `~/.vela/auth.json` (pi's format, mode 0600) and take precedence over `models.json` and environment variables. OAuth tokens are refreshed before they expire, once across processes. Sign in with ChatGPT is for your own local use: channel sessions (Feishu) refuse it, and its token is only sent to api.openai.com. See [Credentials and /login](docs/models.md#credentials-and-login).
|
|
238
|
+
- Extensions can offer `/login` for their providers (`oauth`, `apiKeyLogin`) and read the credential per request with `ctx.credential()`. The SDK has `createVela({ credentials })`, `fileCredentialStore()`, `memoryCredentialStore()`, `vela.login()`, `vela.logout()` and `vela.loginProviders()`. See [Extensions](docs/extensions.md#registerprovider).
|
|
239
|
+
- **Model switching like pi**: the `/model` picker filters as you type, Ctrl+P / Shift+Ctrl+P cycle through the models in the new `enabledModels` setting (all available models when unset), and RPC gains `cycle_model` and `cycle_thinking_level`. See [Enabled models](docs/settings.md#enabled-models).
|
|
240
|
+
- `session.availableThinkingLevels()` lists the levels the current model supports.
|
|
241
|
+
- **`/rag clear`** deletes every chunk of the knowledge base, and `/rag` shows which embedder built it.
|
|
242
|
+
- `EmbeddingFn` has an optional `id` naming its model (set by `createEmbedder()`), and `createFauxEmbedder()` takes an `id` option.
|
|
243
|
+
|
|
244
|
+
### Changed
|
|
245
|
+
|
|
246
|
+
- Tools no longer share one lock across all sessions: `write_file` / `edit_file` queue per file, `bash` takes no lock, and a `sequential` tool only waits for calls in its own session. A long `bash` in one session no longer holds back other sessions (including channel senders).
|
|
247
|
+
- `bash` returns the last 2,000 lines or 50KB of output (was the last 3,000 characters) and ends with `Command exited with code N`, `Command timed out after N seconds` or `Command aborted` instead of `exit=N`.
|
|
248
|
+
- `read_file` pages default to 2,000 lines and at most 50KB (was 200 lines and 8,000 characters).
|
|
249
|
+
|
|
250
|
+
## [0.1.0] - 2026-10-09
|
|
251
|
+
|
|
252
|
+
First public release.
|
|
253
|
+
|
|
254
|
+
### New Features
|
|
255
|
+
|
|
256
|
+
- **Embeddable agent SDK**: `createVela()` assembles the model, tools, system prompt and extensions; `vela.session(id)` opens independent sessions that can run at the same time in one process. Sessions stream typed events, accept `steer` and `followUp` messages while running, and can switch model and thinking level between prompts. See [SDK](docs/sdk.md).
|
|
257
|
+
- **Terminal agent**: the `vela` command runs an interactive TUI built on pi-tui, a print mode (`-p`, piped stdin), a JSON event stream (`--mode json`) and a JSONL RPC mode (`--mode rpc`), plus `--help` and `--version`. See [CLI](docs/cli.md), [JSON mode](docs/json.md) and [RPC mode](docs/rpc.md).
|
|
258
|
+
- **pi-style extensions**: an extension is a function that registers tools, slash commands, model providers and channels and handles agent events. The CLI loads `.ts` extensions on Bun and on Node 22.18+ through Node's type stripping. See [Extensions](docs/extensions.md).
|
|
259
|
+
- **Built-in extensions**: cross-session memory, a RAG knowledge base (sqlite-vec + FTS5 hybrid search), web fetch and search, and a Feishu channel. See [Built-in extensions](docs/built-in-extensions.md) and [Channels](docs/channels.md).
|
|
260
|
+
- **Models and settings**: built-in `openai` and `anthropic` providers, more providers through `~/.vela/models.json`, thinking levels, and user and project `settings.json`; project settings, extensions and skills load only after you trust the project. See [Models](docs/models.md) and [Settings](docs/settings.md).
|
|
261
|
+
- **Long sessions**: tool result truncation, microcompaction and LLM summary compaction, loop detection, retries with backoff, and token and cost tracking (models with no known price show tokens only). Sessions are saved as versioned checkpoints. See [Sessions](docs/sessions.md) and [Session format](docs/session-format.md).
|
|
262
|
+
- **Offline testing**: `@glows777/vela/testing` provides a scripted faux model, a faux embedder, `createTestVela()`, and recording and replay of real runs; the CLI also has an offline demo model (`VELA_MODEL=mock`). See [Testing](docs/testing.md).
|
|
263
|
+
- **Session roles**: `owner`, `collaborator` and `guest` limit which tools a session can use, so channel senders don't get access to the machine; per-session `permissions` can only make a role stricter. Channels give each sender one session per conversation. See [Security](docs/security.md).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 glows777
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @glows777/vela-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Vela MCP extension and configuration descriptor.
|
|
4
|
+
|
|
5
|
+
See the [repository documentation](https://github.com/glows777/Vela/tree/main/docs).
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP server configuration (like pi's `extensions/mcp/config.ts`): the `mcpServers` format of
|
|
3
|
+
* Claude Desktop / Claude Code, read from `~/.vela/mcp.json` and the trusted project's
|
|
4
|
+
* `.vela/mcp.json`. Invalid entries are reported and skipped; they don't stop other servers.
|
|
5
|
+
*/
|
|
6
|
+
import type { ExtensionConfigDefinition } from '@glows777/vela-core/extension-api';
|
|
7
|
+
/**
|
|
8
|
+
* How the model reaches a server's tools. Vela has no codemode yet, so `codemode` (pi's default) is
|
|
9
|
+
* rejected; `deferred` is the default until it exists.
|
|
10
|
+
*/
|
|
11
|
+
export type McpExposure = 'direct' | 'deferred' | 'hidden';
|
|
12
|
+
interface McpCommonConfig {
|
|
13
|
+
/** Per-request timeout in seconds (default 60); progress notifications reset it */
|
|
14
|
+
timeout?: number;
|
|
15
|
+
/** `false` keeps the entry without connecting */
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
/** Exposure of the server's tools (default `deferred`) */
|
|
18
|
+
exposure?: McpExposure;
|
|
19
|
+
/** Per-tool exposure: exact tool names or `*` patterns; exact names win, then the first matching pattern */
|
|
20
|
+
toolExposure?: Record<string, McpExposure>;
|
|
21
|
+
/** What the server offers, in a sentence; listed in the system prompt (default: first line of the server instructions) */
|
|
22
|
+
description?: string;
|
|
23
|
+
}
|
|
24
|
+
/** A local server started as a child process. */
|
|
25
|
+
export interface McpStdioServerConfig extends McpCommonConfig {
|
|
26
|
+
type?: 'stdio';
|
|
27
|
+
/** One executable (not a shell command line); `~/` names the home directory */
|
|
28
|
+
command: string;
|
|
29
|
+
args?: string[];
|
|
30
|
+
env?: Record<string, string>;
|
|
31
|
+
/** Working directory, relative to the Vela's cwd */
|
|
32
|
+
cwd?: string;
|
|
33
|
+
}
|
|
34
|
+
/** A remote server over Streamable HTTP. */
|
|
35
|
+
export interface McpHttpServerConfig extends McpCommonConfig {
|
|
36
|
+
type?: 'http' | 'streamable-http';
|
|
37
|
+
url: string;
|
|
38
|
+
headers?: Record<string, string>;
|
|
39
|
+
}
|
|
40
|
+
export type McpServerConfig = McpStdioServerConfig | McpHttpServerConfig;
|
|
41
|
+
/** A validated server. */
|
|
42
|
+
export interface McpServerEntry {
|
|
43
|
+
name: string;
|
|
44
|
+
config: McpServerConfig;
|
|
45
|
+
/** Where it was configured (a file path, or `options` for `mcp({ servers })`) */
|
|
46
|
+
source: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validates `mcpServers`. Server names that differ only in `-` and `_` count as the same server
|
|
50
|
+
* (their tool names would be the same); the second one is an error.
|
|
51
|
+
*/
|
|
52
|
+
export declare function parseServers(servers: Record<string, unknown>, sources?: Record<string, string>): {
|
|
53
|
+
entries: McpServerEntry[];
|
|
54
|
+
errors: string[];
|
|
55
|
+
};
|
|
56
|
+
/** The exposure of one of the server's tools: exact `toolExposure` names, then the first matching pattern, then the server's. */
|
|
57
|
+
export declare function toolExposureOf(config: McpServerConfig, tool: string): McpExposure;
|
|
58
|
+
/** Raw servers from the mcp.json files, merged, and where each came from. */
|
|
59
|
+
export interface McpServersFile {
|
|
60
|
+
servers: Record<string, unknown>;
|
|
61
|
+
sources: Record<string, string>;
|
|
62
|
+
/** Unreadable files and unset variables */
|
|
63
|
+
errors: string[];
|
|
64
|
+
}
|
|
65
|
+
type Env = Record<string, string | undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* Reads `<agentDir>/mcp.json`, then `<cwd>/.vela/mcp.json` when the project is trusted (like pi). A
|
|
68
|
+
* project entry replaces the user entry of the same name; one without `command`, `url` or `type`
|
|
69
|
+
* only overrides its `enabled`, `exposure` and `toolExposure`. Variables are resolved from `env`.
|
|
70
|
+
*/
|
|
71
|
+
export declare function loadMcpServers(options: {
|
|
72
|
+
agentDir: string;
|
|
73
|
+
projectDir: string;
|
|
74
|
+
trusted: boolean;
|
|
75
|
+
env: Env;
|
|
76
|
+
}): McpServersFile;
|
|
77
|
+
export declare const mcpConfig: ExtensionConfigDefinition;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAA;AAKlF;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAA;AAI1D,UAAU,eAAe;IACvB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,WAAW,CAAA;IACtB,4GAA4G;IAC5G,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAC1C,0HAA0H;IAC1H,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,iDAAiD;AACjD,MAAM,WAAW,oBAAqB,SAAQ,eAAe;IAC3D,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,4CAA4C;AAC5C,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,IAAI,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAA;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC;AAED,MAAM,MAAM,eAAe,GAAG,oBAAoB,GAAG,mBAAmB,CAAA;AAExE,0BAA0B;AAC1B,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,eAAe,CAAA;IACvB,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAA;CACf;AAqFD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC;IAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAoBjD;AAED,iIAAiI;AACjI,wBAAgB,cAAc,CAC5B,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,MAAM,GACX,WAAW,CAeb;AAKD,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAkD7C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;CACT,GAAG,cAAc,CA2DjB;AAED,eAAO,MAAM,SAAS,EAAE,yBAavB,CAAA"}
|