rubino-agent 0.5.1 → 0.5.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +71 -0
- data/docs/agents.md +11 -19
- data/docs/api/v1.md +2 -0
- data/docs/commands.md +3 -6
- data/docs/configuration.md +13 -6
- data/docs/oauth-providers.md +21 -0
- data/lib/rubino/agent/prompts/build.txt +7 -0
- data/lib/rubino/api/operations/tasks/stop_operation.rb +0 -3
- data/lib/rubino/attachments/classify.rb +0 -1
- data/lib/rubino/cli/chat/completion_builder.rb +0 -8
- data/lib/rubino/cli/chat_command.rb +208 -157
- data/lib/rubino/commands/built_ins.rb +0 -1
- data/lib/rubino/commands/executor.rb +1 -7
- data/lib/rubino/commands/handlers/agents.rb +28 -247
- data/lib/rubino/compression/line_skeleton.rb +1 -1
- data/lib/rubino/compression/python_code_skeleton.rb +1 -1
- data/lib/rubino/compression/ruby_code_skeleton.rb +1 -1
- data/lib/rubino/compression/tree_sitter_code_skeleton.rb +1 -1
- data/lib/rubino/config/configuration.rb +22 -10
- data/lib/rubino/config/defaults.rb +42 -20
- data/lib/rubino/context/token_budget.rb +0 -5
- data/lib/rubino/errors.rb +2 -2
- data/lib/rubino/llm/anthropic_role_merge.rb +75 -0
- data/lib/rubino/llm/error_classifier.rb +34 -1
- data/lib/rubino/llm/fake_provider.rb +0 -4
- data/lib/rubino/llm/ruby_llm_adapter.rb +44 -39
- data/lib/rubino/llm/stream_tool_call_recovery.rb +91 -0
- data/lib/rubino/llm/tool_call_recovery.rb +177 -0
- data/lib/rubino/memory/sqlite_extraction_prompt.rb +0 -2
- data/lib/rubino/memory/store.rb +0 -19
- data/lib/rubino/security/pattern_matcher.rb +0 -2
- data/lib/rubino/security/secret_path.rb +16 -4
- data/lib/rubino/skills/registry.rb +16 -2
- data/lib/rubino/tools/background_tasks.rb +25 -216
- data/lib/rubino/tools/base.rb +0 -16
- data/lib/rubino/tools/grep_tool.rb +13 -1
- data/lib/rubino/tools/question_tool.rb +3 -4
- data/lib/rubino/tools/steer_tool.rb +3 -4
- data/lib/rubino/tools/task_stop_tool.rb +5 -7
- data/lib/rubino/tools/task_tool.rb +7 -24
- data/lib/rubino/tools/write_tool.rb +22 -2
- data/lib/rubino/ui/agent_menu.rb +0 -2
- data/lib/rubino/ui/bottom_composer.rb +216 -363
- data/lib/rubino/ui/cli.rb +129 -142
- data/lib/rubino/ui/input_history.rb +0 -5
- data/lib/rubino/ui/live_region.rb +18 -1
- data/lib/rubino/ui/markdown_renderer.rb +47 -3
- data/lib/rubino/ui/markdown_repair.rb +114 -0
- data/lib/rubino/ui/notifier.rb +4 -10
- data/lib/rubino/ui/streaming_markdown.rb +12 -0
- data/lib/rubino/ui/subagent_cards.rb +10 -37
- data/lib/rubino/util/ignore_rules.rb +18 -2
- data/lib/rubino/util/secrets_mask.rb +0 -9
- data/lib/rubino/version.rb +1 -1
- data/lib/rubino.rb +33 -7
- data/rubino-agent.gemspec +1 -0
- metadata +20 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 45e009503b875320e560be8ce46459d7c2a70b0c5744043b70f2121c5e747ab2
|
|
4
|
+
data.tar.gz: c26d1f586ae8578ed3d7298d1f7f8765d16ed5328b6d063d77e0df41d32a710a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dc1cfd93fb51e6236daf8e839a8df248fdcd299aef3028c258fefc93c585bf15e322b5d41ebf4d1b504795a9d24d6e8668f5dece2089b9454f6432dabcace482
|
|
7
|
+
data.tar.gz: 5495d4862d7b082826205ca4ecb30701df4a103ce801233df6a3dc91c59ce423ea4cb6cab4e41b9b0327f72e911ad310bf9433aaf6d218d8803bb0ffd56bec65
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.2.1] - 2026-06-26
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Symlinked workspace roots broke three path checks.** Several modules compared
|
|
8
|
+
a symlink-resolved path against a NON-resolved root, so a workspace reached
|
|
9
|
+
through a symlink (macOS `/etc` → `/private/etc`, `/var` → `/private/var`, or
|
|
10
|
+
any symlinked checkout) defeated the match:
|
|
11
|
+
- **SecretPath** — `secret?("/etc/sudoers")` returned `false` and the
|
|
12
|
+
`~/.ssh`/`~/.aws`/… credential read-gate classified nothing, silently
|
|
13
|
+
no-op'ing the write-approval gate and read-block for those paths.
|
|
14
|
+
- **IgnoreRules** — `git rev-parse --show-toplevel` returns the realpath, so
|
|
15
|
+
the allowed-set rebase dropped *every* file and the whole tree read as
|
|
16
|
+
git-ignored; `grep`/`glob` then returned nothing under a symlinked checkout.
|
|
17
|
+
- **Skills::Registry** — an untrusted repo's project-local `.rubino/skills` was
|
|
18
|
+
not recognised as project-local, so the trust gate failed to drop it (hostile
|
|
19
|
+
project skills could load in an untrusted directory).
|
|
20
|
+
|
|
21
|
+
All three now resolve both sides of the comparison through `realpath` /
|
|
22
|
+
`canonical_path`. Defense-in-depth — not security boundaries.
|
|
23
|
+
- **`grep` Ruby fallback now matches dotfiles.** Without ripgrep on PATH, the
|
|
24
|
+
fallback globbed `**/<include>` without `FNM_DOTMATCH`, so an include like
|
|
25
|
+
`*.env` never matched `.env`/`.envrc` — exactly the secret-bearing files. The
|
|
26
|
+
include glob now matches dotfiles, mirroring `rg --glob`.
|
|
27
|
+
|
|
28
|
+
## [0.5.2] - 2026-06-26
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **Live formatted-markdown streaming.** The in-flight model stream now renders
|
|
33
|
+
as formatted markdown while it arrives (Stage 1), painted as atomic frames via
|
|
34
|
+
DEC-2026 synchronized output so a fast stream never tears mid-update (Stage 2),
|
|
35
|
+
with committed code blocks syntax-highlighted through Rouge (Stage 3). (#592,
|
|
36
|
+
#593, #594)
|
|
37
|
+
- **Leaked tool-call recovery.** Models that emit a tool call as plain text or
|
|
38
|
+
garbled XML/JSON markup instead of a structured call (MiniMax-M3 and other
|
|
39
|
+
tool-loop models) now have those calls re-parsed into real `tool_calls` at the
|
|
40
|
+
transport layer so they actually execute, including a garbled `<invoke">`
|
|
41
|
+
variant.
|
|
42
|
+
- **`write` content preview.** The `write` tool box now shows a preview of the
|
|
43
|
+
content being written.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- Raise the default `max_tool_iterations` from 25 to 90 (Hermes-aligned), so long
|
|
48
|
+
tool-driven turns no longer hit the ceiling mid-task.
|
|
49
|
+
- Teach the agent (via the build prompt) to read the compressed tool-output
|
|
50
|
+
markers introduced in 0.5.1.
|
|
51
|
+
|
|
52
|
+
### Fixed
|
|
53
|
+
|
|
54
|
+
- Render any unterminated code fence as a code box, matching CommonMark's
|
|
55
|
+
end-of-file fence auto-close, instead of leaking the raw backticks. (#595)
|
|
56
|
+
- Merge consecutive same-role messages on the Anthropic-family wire so the
|
|
57
|
+
request shape stays valid. (#597)
|
|
58
|
+
- Give MiniMax its full output ceiling so a long thinking block no longer starves
|
|
59
|
+
the visible output (a root cause of heavy-turn "invalid params" death).
|
|
60
|
+
- Keep a 5xx-wrapped "invalid params" response on the retryable path.
|
|
61
|
+
- Multi-line `ask()` prompts no longer erase terminal scrollback.
|
|
62
|
+
- Exclude synthetic `[harness control]` injections from the rewind picker.
|
|
63
|
+
- Fix an installed-gem launch crash (`uninitialized constant Rubino::TAGLINE`).
|
|
64
|
+
|
|
65
|
+
### Removed
|
|
66
|
+
|
|
67
|
+
- Drop the dead `server.*` config section, the orphaned `ask_parent` takeover and
|
|
68
|
+
ask/reply substrate, and dead code surfaced by the post-removal audit.
|
|
69
|
+
|
|
70
|
+
### Docs
|
|
71
|
+
|
|
72
|
+
- Mark native OAuth as not wired end-to-end (WIP).
|
|
73
|
+
|
|
3
74
|
## [0.5.1] - 2026-06-25
|
|
4
75
|
|
|
5
76
|
### Added
|
data/docs/agents.md
CHANGED
|
@@ -4,7 +4,7 @@ rubino has two distinct multi-agent surfaces, and **both ship today**:
|
|
|
4
4
|
|
|
5
5
|
1. **Background subagents** (✅ shipping) — the agent delegates bounded sub-tasks
|
|
6
6
|
to isolated subagent runs via its `task` tool, and you supervise them with
|
|
7
|
-
`/agents
|
|
7
|
+
`/agents`.
|
|
8
8
|
2. **Primary-agent switching** (✅ shipping) — pick the primary agent that
|
|
9
9
|
handles your turns: `/agent <name>` (or a bare `/<name>` for a primary)
|
|
10
10
|
pins it for the session, **Tab** cycles through the primaries, and a one-shot
|
|
@@ -68,9 +68,7 @@ message instead of fanning out unbounded work:
|
|
|
68
68
|
| Glyph | Status | Meaning | You act via |
|
|
69
69
|
|---|---|---|---|
|
|
70
70
|
| `●` | `running` | Working (last activity shown) | — |
|
|
71
|
-
| `●` | `needs_approval` | A child tool needs your approval (or a budget request) | `/agents <id>`
|
|
72
|
-
| `⛔` | `blocked_on_human` | Vocabulary glyph for a child parked on the human (not raised in normal operation now that subagents are non-blocking) | `/reply <id> <answer>` |
|
|
73
|
-
| `◷` | `blocked_on_parent` | Vocabulary glyph for a child parked on its agent-parent (likewise not raised now that subagents are non-blocking) | (optional) `/reply <id>` |
|
|
71
|
+
| `●` | `needs_approval` | A child tool needs your approval (or a budget request) | `/agents <id>` |
|
|
74
72
|
| `◌` | `stopping` | Stop requested; unwinding at its next checkpoint | — |
|
|
75
73
|
| `✓` | `done` | Finished; result available | `/agents <id>` |
|
|
76
74
|
| `✗` | `failed` | Errored; error available | `/agents <id>` |
|
|
@@ -79,25 +77,20 @@ message instead of fanning out unbounded work:
|
|
|
79
77
|
Subagents are **non-blocking** background workers: they never pause to ask you a
|
|
80
78
|
mid-task question. The one way a child waits on you is an **approval** — its next
|
|
81
79
|
tool needs your go-ahead, so it parks as `needs_approval` and a marker persists
|
|
82
|
-
until you resolve it (via `/agents <id>`
|
|
83
|
-
blocked_on_human` / `◷ blocked_on_parent` glyphs remain in the status vocabulary
|
|
84
|
-
the `/agents` surface can render, but with the child→parent ask channel removed
|
|
85
|
-
they are no longer raised in normal operation.
|
|
80
|
+
until you resolve it (via `/agents <id>`).
|
|
86
81
|
|
|
87
|
-
### Supervising from the CLI: `/agents`
|
|
82
|
+
### Supervising from the CLI: `/agents`
|
|
88
83
|
|
|
89
84
|
```
|
|
90
85
|
/agents # list background subagents (status, tools run, activity)
|
|
91
86
|
/agents <id> # drill in: live watch while running, result/error when done
|
|
92
|
-
/agents <id> --stop # cancel a running subagent
|
|
87
|
+
/agents <id> --stop # cancel a running subagent
|
|
93
88
|
/agents <id> steer "note" # park a note folded into the child's context at its next turn
|
|
94
89
|
/agents <id> probe "question" # ephemeral read-only peek — nothing is saved to the child
|
|
95
|
-
/reply <id> <answer> # answer a child blocked on you (e.g. an approval)
|
|
96
|
-
/reply # bare: list the subagents currently blocked on you
|
|
97
90
|
```
|
|
98
91
|
|
|
99
|
-
`/tasks` is an alias for `/agents`. Stopping a node cancels its
|
|
100
|
-
|
|
92
|
+
`/tasks` is an alias for `/agents`. Stopping a node cancels its approval gate so
|
|
93
|
+
a parked child unwinds at once.
|
|
101
94
|
|
|
102
95
|
#### Attach to a subagent (agent-view)
|
|
103
96
|
|
|
@@ -109,12 +102,11 @@ subagent picker, arrow to one, and `Enter`:
|
|
|
109
102
|
what it said, replayed from its session (not the bounded activity snapshot the
|
|
110
103
|
picker used to show);
|
|
111
104
|
- the prompt becomes **scoped** to it: `sa_xxxx ❯`;
|
|
112
|
-
- while attached, just **type** to steer the running child
|
|
113
|
-
|
|
114
|
-
to the main timeline.
|
|
105
|
+
- while attached, just **type** to steer the running child — no id needed; `←` on
|
|
106
|
+
the empty prompt (or `/detach`) returns to the main timeline.
|
|
115
107
|
|
|
116
|
-
So attaching makes `/agents <id> steer/probe`
|
|
117
|
-
|
|
108
|
+
So attaching makes `/agents <id> steer/probe` redundant for the focused child —
|
|
109
|
+
they're the same operations, just addressed by id. Attach is a
|
|
118
110
|
between-turns action (it owns the screen): while a parent turn is still streaming
|
|
119
111
|
the picker's `Enter` toasts "attach when the turn ends" — attach once it's idle.
|
|
120
112
|
|
data/docs/api/v1.md
CHANGED
|
@@ -350,6 +350,8 @@ Cooperative cancel of a running task (descendant ask-gates are cancelled too). R
|
|
|
350
350
|
|
|
351
351
|
## OAuth
|
|
352
352
|
|
|
353
|
+
> **WIP — not wired end-to-end.** These endpoints work and store encrypted tokens, but **no tool consumes a connection's token yet** and there is no CLI surface. See the status banner in [`docs/oauth-providers.md`](../oauth-providers.md) (and issue #590: native vs MCP-delegated).
|
|
354
|
+
|
|
353
355
|
See [`docs/oauth-providers.md`](../oauth-providers.md) for the full PKCE flow, encryption key requirements, and per-provider setup. The HTTP surface:
|
|
354
356
|
|
|
355
357
|
### `GET /v1/oauth/providers` → 200
|
data/docs/commands.md
CHANGED
|
@@ -179,7 +179,6 @@ Type these inside `rubino chat`. Generated from `BuiltIns::DESCRIPTIONS` (drift-
|
|
|
179
179
|
| `/agent` | Switch the primary agent (/agent <name>; a bare /<name> or Tab cycles) |
|
|
180
180
|
| `/agents` | List background subagents; ↓+Enter to attach & steer one live, or steer/probe/view by id |
|
|
181
181
|
| `/tasks` | Alias for /agents |
|
|
182
|
-
| `/reply` | Answer a subagent that is blocked waiting on you (e.g. an approval) |
|
|
183
182
|
| `/stop` | Stop a running subagent (/stop <id>; alias for /agents <id> --stop) |
|
|
184
183
|
| `/jobs` | List the background job queue (status counts); /jobs <id> for detail |
|
|
185
184
|
| `/skills` | List skills; activate one ('none' clears), or enable/disable NAME |
|
|
@@ -317,7 +316,7 @@ Read (and set) configuration without leaving the REPL, over the same **effective
|
|
|
317
316
|
|
|
318
317
|
Gets resolve default-valued keys (not just what's in the file), and secret-named keys (`api_key`, tokens, …) render masked — exactly like `rubino config show`. A set writes through `Config::Writer` (the same persist path `/reasoning` and `/think` use) **and** updates the live configuration, so it survives the session and applies from the next turn; consumers that memoize their config (e.g. the memory backend) still need a restart. Typing `/config ` opens a dropdown with the verbs plus the known config keys flattened from the defaults tree; after `get`/`set` the keys complete again.
|
|
319
318
|
|
|
320
|
-
### Background subagents: `/agents`
|
|
319
|
+
### Background subagents: `/agents`
|
|
321
320
|
|
|
322
321
|
The agent spawns background subagents with its `task` tool; these commands are the human surface over them (full model in [agents.md](agents.md)):
|
|
323
322
|
|
|
@@ -327,8 +326,6 @@ The agent spawns background subagents with its `task` tool; these commands are t
|
|
|
327
326
|
/agents <id> --stop # cancel a running subagent (blocked descendants unwind too)
|
|
328
327
|
/agents <id> steer "note" # park a note folded into the child's context at its next turn
|
|
329
328
|
/agents <id> probe "question" # ephemeral read-only peek — nothing is saved to the child
|
|
330
|
-
/reply <id> <answer> # answer a subagent blocked on you (e.g. an approval)
|
|
331
|
-
/reply # bare: list the subagents currently blocked on you
|
|
332
329
|
```
|
|
333
330
|
|
|
334
331
|
`/tasks` is an alias for `/agents`.
|
|
@@ -337,9 +334,9 @@ The agent spawns background subagents with its `task` tool; these commands are t
|
|
|
337
334
|
idle prompt to open the subagent picker, arrow to one, and `Enter` to **attach**:
|
|
338
335
|
the screen switches to that agent's own full timeline (its tool calls and what it
|
|
339
336
|
said, replayed) and the prompt becomes scoped — `sa_xxxx ❯`. While attached, just
|
|
340
|
-
type to steer the running child
|
|
337
|
+
type to steer the running child; `←` on the
|
|
341
338
|
empty prompt (or `/detach`) returns to the main timeline. The scoped prompt makes
|
|
342
|
-
the global `/agents <id> steer/probe`
|
|
339
|
+
the global `/agents <id> steer/probe` forms redundant — they're
|
|
343
340
|
the same operations, by id, from anywhere.
|
|
344
341
|
|
|
345
342
|
### Workspace roots: `/add-dir` and `/dirs`
|
data/docs/configuration.md
CHANGED
|
@@ -227,6 +227,9 @@ display:
|
|
|
227
227
|
statusbar: true # the model + context bar under the chat input
|
|
228
228
|
tool_output_preview_lines: 3 # head lines of tool output shown in the transcript (0 = full dump)
|
|
229
229
|
input_max_rows: 8 # chat input grows up to this many rows, then scrolls
|
|
230
|
+
live_markdown: true # format the in-flight streamed block live (false = raw live tail)
|
|
231
|
+
synchronized_output: true # atomic frames via DEC-2026 BSU/ESU (false = legacy per-write frames)
|
|
232
|
+
code_highlight: true # syntax-highlight committed code blocks (Rouge); false = plain
|
|
230
233
|
|
|
231
234
|
paste:
|
|
232
235
|
collapse_lines: 5 # pastes longer than this collapse to a placeholder
|
|
@@ -246,6 +249,10 @@ context:
|
|
|
246
249
|
- `display.statusbar` (default `true`) pins a dim one-line bar UNDER the chat input — the session mode first (plus the branch/skill tokens when set), then the resolved model id and context saturation, e.g. `default · MiniMax-M3 · ctx ~8.4k/64k (13%)` (the percentage is omitted below 1%). The mode token is the live mode indicator (the prompt itself is a constant `▍❯ `): dim `default`, yellow `plan`, red `yolo`. Saturation uses the REAL usage the provider reported for the last response when available (the full assembled prompt, recorded by the agent loop), else the same chars/4 estimate compaction runs on (`Context::TokenBudget`); the window comes from `model.context_length` / `context.max_tokens`. It refreshes at turn boundaries (after each turn footer, and on session resume), never per stream delta. The percentage turns yellow at 70% and red at 90%; with no usable window only the token count shows. The bar is omitted off a TTY or on terminals narrower than 40 columns.
|
|
247
250
|
- `display.tool_output_preview_lines` (default `3`) caps how many head lines of each tool's output the transcript shows before a dim `… +N lines (full output → context)` marker. DISPLAY-ONLY: the model always receives the full output (subject to the `tool_output` truncation caps) — only the scrollback rendering collapses. Set `0` to restore the old full dump.
|
|
248
251
|
- `display.input_max_rows` (default `8`) caps how many visual rows the chat input grows to as a long or multi-line prompt wraps; past the cap the input scrolls vertically, keeping the caret row in view.
|
|
252
|
+
- `display.live_markdown` (default `true`) renders the still-streaming (in-flight) block as FORMATTED markdown in the live region — bold, headings, lists and code style as the tokens arrive, with syntax left open by the partial stream repaired (an open code fence shows as a code block, a dangling `**`/`` ` `` span is closed) so no raw marker leaks. Set `false` for the legacy raw rolling-tail that only snaps to styled when the block commits. Display-only; the committed scrollback render is identical either way.
|
|
253
|
+
- `display.synchronized_output` (default `true`) wraps each live-region frame in DEC private mode 2026 (BSU/ESU synchronized output) so a supporting terminal (kitty, WezTerm, tmux ≥3.4, recent xterm.js) buffers the whole clear→commit→redraw sequence and swaps it in one atomic update — no flicker or tearing on multi-step repaints. Terminals without support silently ignore the mode (it degrades cleanly); the escapes are emitted only to a real TTY. Set `false` for the legacy per-write frames.
|
|
254
|
+
- `display.code_highlight` (default `true`) syntax-highlights fenced code blocks by language (via Rouge) in the COMMITTED render — the live tail stays unstyled, so highlighting never blocks the stream (code shows instantly, colours arrive a beat later when the block commits, like Claude Code). Unknown languages, language-less fences, and any failure fall back to the plain code body. Set `false` for plain (uncoloured) code blocks.
|
|
255
|
+
- An **unterminated** code fence at end-of-stream — a fence the model never closed, or closed with a too-short bare run of backticks (e.g. MiniMax-M3 emitting `` against a ``` opener) — is rendered as a code box, matching CommonMark's end-of-document auto-close (§4.5) that every other renderer relies on. The CLI synthesises the close at the opener length (never relaxing the "close ≥ opener" rule), because kramdown does not auto-close an open fence.
|
|
249
256
|
- `paste.collapse_lines` (default `5`) — the file-backed paste pipeline's first tier. Pasting MORE than this many lines into the chat input inserts a single cyan `[Pasted text #N +M lines]` placeholder instead of flooding the composer; the placeholder is one editable token (backspace deletes it whole, you can type around it, it survives ↑ draft recall and Alt+Enter queueing) and expands to the full pasted body when the message is sent — the model sees everything, while the transcript echo keeps the compact placeholder. Pastes at or under the threshold inline as real rows, exactly as before.
|
|
250
257
|
- `paste.file_threshold_tokens` (default `8000`) — the second tier. A paste estimated above this many tokens (chars/4, the same rule compaction uses) is written to `<RUBINO_HOME>/sessions/<session-id>/paste_N.txt` instead of being held inline, and the sent message carries `[Pasted text #N saved to <path> — too large to inline; read it with the read tool]` so the model reads just the parts it needs. The files persist for the session; `/clear-images` does not touch them (it only drops staged image attachments).
|
|
251
258
|
|
|
@@ -303,7 +310,6 @@ tasks:
|
|
|
303
310
|
max_children_per_node: 3 # max LIVE direct children per node
|
|
304
311
|
max_concurrent_total: 8 # hard ceiling on total LIVE subagents across the tree
|
|
305
312
|
max_live_probes_per_child: 5 # per-child budget for billed live probes (probe(live: true))
|
|
306
|
-
ask_parent_timeout: 900 # vestigial: governed the removed child→parent ask channel; no effect now
|
|
307
313
|
```
|
|
308
314
|
|
|
309
315
|
### tools
|
|
@@ -657,13 +663,14 @@ agents:
|
|
|
657
663
|
mcp_servers: []
|
|
658
664
|
```
|
|
659
665
|
|
|
660
|
-
###
|
|
666
|
+
### api
|
|
661
667
|
|
|
662
|
-
|
|
663
|
-
server
|
|
664
|
-
|
|
665
|
-
|
|
668
|
+
The API server's listen port and bind host come from the CLI, not config:
|
|
669
|
+
`rubino server --port <n>` (or `RUBINO_API_PORT`, default `4820`) and `--host`
|
|
670
|
+
(or `RUBINO_API_HOST`). The bearer token is `RUBINO_API_KEY`. The `api` block
|
|
671
|
+
configures payload caps, rate limiting, and the public-bind gate:
|
|
666
672
|
|
|
673
|
+
```yaml
|
|
667
674
|
api:
|
|
668
675
|
max_body_bytes: 5242880 # 5 MB cap on JSON request bodies (413 past this)
|
|
669
676
|
max_upload_bytes: 52428800 # 50 MB cap on multipart uploads
|
data/docs/oauth-providers.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# OAuth provider connectors
|
|
2
2
|
|
|
3
|
+
> **Status: NOT WIRED END-TO-END (WIP).** The pieces below exist and the HTTP
|
|
4
|
+
> surface works — the `/v1/oauth/...` API endpoints perform the PKCE flow and
|
|
5
|
+
> store **encrypted** tokens in the `oauth_connections` table. But the subsystem
|
|
6
|
+
> is **API-only and not yet consumed**:
|
|
7
|
+
> - **No tool uses the stored tokens.** Nothing reads `ConnectionRepository`
|
|
8
|
+
> outside the API operations — there is no `GithubTool`/`GoogleTool` etc. that
|
|
9
|
+
> pulls a connection's token to call a provider, so a connected account is not
|
|
10
|
+
> actually actionable by the agent yet.
|
|
11
|
+
> - **No CLI surface.** There is no `rubino oauth` command; the connect/callback
|
|
12
|
+
> flow needs a browser redirect, so it lives only on the API. The CLI treats
|
|
13
|
+
> `RUBINO_ENCRYPTION_KEY` as optional (`doctor`: "only needed for the
|
|
14
|
+
> API/OAuth server").
|
|
15
|
+
> - **Token sharing, when consumption lands:** tokens are not "passed" between
|
|
16
|
+
> CLI and API — both read the **same SQLite DB** (same `RUBINO_HOME`) and
|
|
17
|
+
> decrypt with the **same `RUBINO_ENCRYPTION_KEY`**. So wiring CLI consumption
|
|
18
|
+
> = read `ConnectionRepository` + require the key on the CLI too.
|
|
19
|
+
>
|
|
20
|
+
> Open design question (issue #590): finish the native subsystem, or deprecate
|
|
21
|
+
> it and delegate third-party connections to an MCP server (which does its own
|
|
22
|
+
> OAuth and holds its own tokens). Don't depend on native OAuth in production yet.
|
|
23
|
+
|
|
3
24
|
Built-in OAuth integration lets users connect third-party accounts (Github, Google, etc.) so tools running inside rubino can act on their behalf.
|
|
4
25
|
|
|
5
26
|
## Design
|
|
@@ -38,6 +38,13 @@ assume or default to one.
|
|
|
38
38
|
map-reduces the file in a separate context and returns only the summary,
|
|
39
39
|
so the raw text never fills this conversation. Reach for `read` (with
|
|
40
40
|
offset/limit) or `grep` only when you need exact lines, not an overview.
|
|
41
|
+
- Tool output may be COMPRESSED to save context — it is lossless to YOU: a
|
|
42
|
+
`# … N lines elided — read <path> offset=.. limit=..` pointer in a file read
|
|
43
|
+
means that exact body is one targeted `read` away, verbatim (so issue that
|
|
44
|
+
read before editing it). `[… N lines hidden by log compression …]` in command
|
|
45
|
+
output means only passing/info noise was dropped — every error/failure and the
|
|
46
|
+
final summary are kept. `{"_elided": N}` / `"<elided N chars>"` mark trimmed
|
|
47
|
+
JSON. These markers are NOT part of the file; never match or edit against them.
|
|
41
48
|
- The `ruby` tool runs sandboxed Ruby for quick computation/scripting —
|
|
42
49
|
reach for it when Ruby fits the project. Otherwise use `shell` for the
|
|
43
50
|
host's binaries and the project's own toolchain (its interpreter, package
|
|
@@ -31,9 +31,6 @@ module Rubino
|
|
|
31
31
|
raise ConflictError, "task #{id} already #{entry.status} — nothing to stop" unless entry.status == :running
|
|
32
32
|
|
|
33
33
|
entry.runner&.cancel!
|
|
34
|
-
# Stop-cascade (S5a): wake any descendant parked on a blocking
|
|
35
|
-
# ask_parent so the whole subtree unwinds at once.
|
|
36
|
-
@registry.cancel_descendant_ask_gates(id)
|
|
37
34
|
[202, Serializer.detail(entry)]
|
|
38
35
|
end
|
|
39
36
|
end
|
|
@@ -33,7 +33,6 @@ module Rubino
|
|
|
33
33
|
application/x-7z-compressed application/x-rar-compressed application/vnd.rar
|
|
34
34
|
application/x-bzip2 application/x-xz
|
|
35
35
|
].freeze
|
|
36
|
-
IMAGE_EXTS = %w[.png .jpg .jpeg .gif .webp .bmp .tiff .tif].freeze
|
|
37
36
|
|
|
38
37
|
# Leading magic bytes per recognised image/document MIME (WebP is
|
|
39
38
|
# special-cased: RIFF container + WEBP tag). Marcel lets the file NAME
|
|
@@ -62,7 +62,6 @@ module Rubino
|
|
|
62
62
|
# * /agents (alias /tasks) — the live subagent ids, then the
|
|
63
63
|
# steer/probe/--stop subcommand grammar, so the comm surface is
|
|
64
64
|
# discoverable from the composer (#39).
|
|
65
|
-
# * /reply — the ids of children blocked waiting on the human.
|
|
66
65
|
# * /mcp — the configured server names (+ reload), then on/off for a
|
|
67
66
|
# named server (#182), same grammar shape as /agents.
|
|
68
67
|
# * /mode, /reasoning, /think — the closed enums (#185), via the
|
|
@@ -95,7 +94,6 @@ module Rubino
|
|
|
95
94
|
"agents" => ->(args) { agents_arg_candidates(args) },
|
|
96
95
|
"tasks" => ->(args) { agents_arg_candidates(args) },
|
|
97
96
|
"agent" => ->(args) { args.empty? ? primary_agent_names : [] },
|
|
98
|
-
"reply" => ->(args) { args.empty? ? blocked_subagent_ids : [] },
|
|
99
97
|
"mcp" => ->(args) { mcp_arg_candidates(args) },
|
|
100
98
|
"mode" => ->(args) { args.empty? ? Rubino::Modes::ALL.map(&:to_s) : [] },
|
|
101
99
|
"model" => ->(args) { args.empty? ? model_arg_candidates : [] },
|
|
@@ -148,12 +146,6 @@ module Rubino
|
|
|
148
146
|
end
|
|
149
147
|
end
|
|
150
148
|
|
|
151
|
-
# Children parked on an ask_parent waiting for the human — the ids /reply
|
|
152
|
-
# answers.
|
|
153
|
-
def blocked_subagent_ids
|
|
154
|
-
Tools::BackgroundTasks.instance.awaiting_human.map(&:id)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
149
|
# The /model candidates: the registry's model ids for the provider the
|
|
158
150
|
# next turn would route through. Resolved lazily on each dropdown open so
|
|
159
151
|
# a /model or /config provider switch is reflected immediately.
|