@bivy/bivy 0.20.1-staging.3 → 0.20.1-staging.5
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/bin/bivy.mjs
CHANGED
|
@@ -2521,18 +2521,18 @@ async function cmdContext(args = []) {
|
|
|
2521
2521
|
console.log(`\nEverything else: ${c.cyan("bivy help")} (or ${c.cyan("bivy help --json")}).`);
|
|
2522
2522
|
}
|
|
2523
2523
|
|
|
2524
|
-
// `bivy suggest "<task>" [--title "…"] [--session <id>]`
|
|
2525
|
-
// user can start in one tap,
|
|
2524
|
+
// `bivy suggest "<task>" [--title "…"] [--run here|subagents|new] [--session <id>]`
|
|
2525
|
+
// — propose a task the user can start in one tap, in this session or beside it. For an agent
|
|
2526
2526
|
// offering next steps: write the task as a complete instruction.
|
|
2527
2527
|
async function cmdSuggest(args = []) {
|
|
2528
|
-
const usage = 'Usage: bivy suggest "<task>" [--title "short label"] [--session <id>] [--json]';
|
|
2528
|
+
const usage = 'Usage: bivy suggest "<task>" [--title "short label"] [--run here|subagents|new] [--session <id>] [--json]';
|
|
2529
2529
|
if (args.includes("-h") || args.includes("--help")) {
|
|
2530
|
-
console.log(`${usage}\n\nPost a task the user can start in one tap
|
|
2530
|
+
console.log(`${usage}\n\nPost a task the user can start in one tap: in this session, through your sub-agents, or in a parallel session that works in its own copy of the project. Write it as a complete instruction, with paths relative to the project root.\n\n--run says where you recommend running it, which becomes the card's main button: here (builds on this conversation), subagents (independent tasks you can split across your own sub-agents; only if you have them), or new (bigger independent work the user will want to follow in its own session). Without it, one card recommends here and several recommend new.\n\n--json prints {"ok","id"}.`);
|
|
2531
2531
|
return;
|
|
2532
2532
|
}
|
|
2533
2533
|
const json = wantsJson(args);
|
|
2534
2534
|
const fail = (error) => cliError(error, { json, paint: c.red });
|
|
2535
|
-
const flagsWithValue = new Set(["--session", "--title"]);
|
|
2535
|
+
const flagsWithValue = new Set(["--session", "--title", "--run"]);
|
|
2536
2536
|
const flag = (name) => {
|
|
2537
2537
|
const i = args.indexOf(name);
|
|
2538
2538
|
return i >= 0 && i + 1 < args.length ? args[i + 1] : undefined;
|
|
@@ -2540,10 +2540,12 @@ async function cmdSuggest(args = []) {
|
|
|
2540
2540
|
const text = args.filter((a, i) => !a.startsWith("-") && !(i > 0 && flagsWithValue.has(args[i - 1]))).join(" ").trim();
|
|
2541
2541
|
const sessionId = resolveAttachSessionId({ sessionFlag: flag("--session"), env: process.env });
|
|
2542
2542
|
if (!text) return fail({ code: "usage", message: usage, exit: EXIT.usage });
|
|
2543
|
+
const run = flag("--run");
|
|
2544
|
+
if (run !== undefined && !["here", "subagents", "new"].includes(run)) return fail({ code: "usage", message: `--run is here, subagents or new (got "${run}").`, exit: EXIT.usage });
|
|
2543
2545
|
if (!sessionId) return fail({ code: "no_session", message: "No session id.", hint: "Run inside an agent session ($BIVY_SESSION_ID) or pass --session <id>.", next: "bivy sessions --json", exit: EXIT.usage });
|
|
2544
2546
|
const config = loadConfig();
|
|
2545
2547
|
if (!(await ensureNodeRunning(config))) return fail({ code: "node_unreachable", message: `Could not reach the Bivy node at ${url(config)}.`, next: "bivy status", exit: EXIT.unavailable });
|
|
2546
|
-
const res = await sessionPost(config, sessionId, "suggest", { text, title: flag("--title") }).catch((error) => error);
|
|
2548
|
+
const res = await sessionPost(config, sessionId, "suggest", { text, title: flag("--title"), run }).catch((error) => error);
|
|
2547
2549
|
if (res instanceof Error) return fail({ code: "node_unreachable", message: `Could not reach the Bivy node: ${res.message}`, next: "bivy status", exit: EXIT.unavailable });
|
|
2548
2550
|
const body = await res.json().catch(() => ({}));
|
|
2549
2551
|
if (!res.ok) return fail(sessionHttpError("Suggest", res.status, body));
|
package/bin/cli-commands.mjs
CHANGED
|
@@ -48,8 +48,8 @@ export const COMMANDS = [
|
|
|
48
48
|
tools: [{ name: "notify_user", description: "Send the user a message: a card in the chat, and a push to their phone when nobody has the app open (the push names the session, not the text). Use it when long work finishes, you are blocked, or something needs their attention. At most one push a minute per session.", input: { message: { type: "string", description: "What to tell the user.", required: true }, urgent: { type: "boolean", description: "Push even while the user has the app open." } }, argv: ["notify", { arg: "message" }, { when: "urgent", flag: "--urgent" }] }] },
|
|
49
49
|
{ name: "ask", group: "session", scope: "session", json: true, usage: 'ask "<question>" [--option A --option B] [--async]', summary: "Ask the user a question and wait for the answer", subcommands: ["status", "wait"],
|
|
50
50
|
tools: [{ name: "ask_user", description: "Ask the user a question in the chat and wait for their answer (their phone gets a push). Offer 2-8 options, or none for a free-text answer; they can always write their own. Returns {status, answer}: status is answered, dismissed (decide yourself) or expired.", input: { question: { type: "string", description: "The question.", required: true }, options: { type: "array", items: { type: "string" }, description: "Choices to pick from (2-8)." }, multiple: { type: "boolean", description: "Allow several choices." }, header: { type: "string", description: "A short label for the card." }, timeout_seconds: { type: "number", description: "How long to wait (default 600)." } }, argv: ["ask", { arg: "question" }, { flag: "--option", from: "options" }, { when: "multiple", flag: "--multi" }, { flag: "--header", from: "header" }, { flag: "--timeout", from: "timeout_seconds" }] }] },
|
|
51
|
-
{ name: "suggest", group: "session", scope: "session", json: true, usage: 'suggest "<task>" [--title "label"]', summary: "Propose a task the user can start in one tap",
|
|
52
|
-
tools: [{ name: "suggest_task", description: "Propose a next step the user can start in one tap
|
|
51
|
+
{ name: "suggest", group: "session", scope: "session", json: true, usage: 'suggest "<task>" [--title "label"] [--run here|subagents|new]', summary: "Propose a task the user can start in one tap",
|
|
52
|
+
tools: [{ name: "suggest_task", description: "Propose a next step the user can start in one tap: in this session, through your sub-agents, or in a parallel session with its own copy of the project. Write the task as a complete instruction with paths relative to the project root. Post one per idea instead of listing them.", input: { task: { type: "string", description: "The complete instruction.", required: true }, title: { type: "string", description: "A short label for the card." }, run: { type: "string", enum: ["here", "subagents", "new"], description: "Where you recommend running it: here (builds on this conversation), subagents (independent tasks you can split across your own sub-agents; only if you have them), or new (bigger independent work in its own session). Defaults: here for one card, new for several." } }, argv: ["suggest", { arg: "task" }, { flag: "--title", from: "title" }, { flag: "--run", from: "run" }] }] },
|
|
53
53
|
{ name: "app", group: "session", scope: "session", json: true, usage: "app <publish|shot|present|share|run|…>", summary: "Live previews: publish a web/terminal/desktop app, screenshot it, present it", subcommands: ["publish", "list", "remove", "shot", "present", "share", "notes", "run", "click", "type", "key", "scroll", "drag", "move", "menu"],
|
|
54
54
|
tools: [
|
|
55
55
|
{ name: "app_publish", description: "Give the user a live preview of something with a UI, from an app manifest file (web server port, static build, terminal, or desktop app). Run `bivy app --help` for the manifest format.", input: { manifest_path: { type: "string", description: "Path to the manifest JSON.", required: true } }, argv: ["app", "publish", { arg: "manifest_path" }] },
|
|
@@ -28,9 +28,18 @@ Ask only when you can't reasonably decide yourself.
|
|
|
28
28
|
|
|
29
29
|
bivy suggest "Add a GET /version endpoint that returns the package version." --title "Add /version"
|
|
30
30
|
|
|
31
|
-
Each suggestion is a card the user can start in one tap
|
|
32
|
-
session with its own copy of the project. Write it as
|
|
33
|
-
paths relative to the project root. Post one per idea
|
|
31
|
+
Each suggestion is a card the user can start in one tap: here, through your
|
|
32
|
+
sub-agents, or in a parallel session with its own copy of the project. Write it as
|
|
33
|
+
a complete instruction with paths relative to the project root. Post one per idea
|
|
34
|
+
instead of a bulleted list.
|
|
35
|
+
|
|
36
|
+
`--run` picks the card's main button; the others stay one tap away:
|
|
37
|
+
|
|
38
|
+
| `--run` | When |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `here` | It builds on this conversation, or it's small. Default for a single card. |
|
|
41
|
+
| `subagents` | Independent tasks you can split across your own sub-agents and report back on. Only if you have sub-agents. |
|
|
42
|
+
| `new` | Bigger independent work the user will want to follow, review or merge on its own. Default for several cards. |
|
|
34
43
|
|
|
35
44
|
## Is anyone there?
|
|
36
45
|
|
|
@@ -39,9 +39,12 @@ export const BIVY_AGENT_NOTE = [
|
|
|
39
39
|
"can check your work; `bivy app present` tells the user a visible change is ready to look at. " +
|
|
40
40
|
"`bivy app --help` has the details.",
|
|
41
41
|
"- Proposing tasks the user could hand you (next steps, ideas, options): post each with " +
|
|
42
|
-
'`bivy suggest "<complete instruction>" [--title "short label"]` instead of only
|
|
43
|
-
"start each in one tap, in this session or in a parallel
|
|
44
|
-
"write it to stand on its own, with paths relative to the
|
|
42
|
+
'`bivy suggest "<complete instruction>" [--title "short label"] [--run here|subagents|new]` instead of only ' +
|
|
43
|
+
"listing them. The user can start each in one tap, in this session, through your sub-agents, or in a parallel " +
|
|
44
|
+
"one that works in its own copy of the project, so write it to stand on its own, with paths relative to the " +
|
|
45
|
+
"project root. `--run` is the one you recommend: here when it builds on this conversation, subagents for " +
|
|
46
|
+
"independent tasks you can split and supervise (only if you have sub-agents), new for bigger work worth its " +
|
|
47
|
+
"own session.",
|
|
45
48
|
"- When you finish long work, get blocked, or need the user to look at something: " +
|
|
46
49
|
'`bivy notify "<message>"` (a chat card, plus a push to their phone when they are away). To ask and wait for ' +
|
|
47
50
|
'an answer: `bivy ask "<question>" [--option A --option B]` prints their answer (or use your own ask-the-user tool).',
|
package/dist/apps/styles.css
CHANGED
|
@@ -3127,8 +3127,14 @@ p.setup-note .btn.link { min-height: 44px; margin-block: calc((1lh - 44px) / 2);
|
|
|
3127
3127
|
.suggestion-eyebrow, .delegation-eyebrow, .notice-eyebrow { margin: 0; color: var(--muted); font-size: var(--text-xs); font-weight: var(--weight-semibold); }
|
|
3128
3128
|
.suggestion-title { margin: 0; font-size: var(--text-base); font-weight: var(--weight-semibold); overflow-wrap: anywhere; }
|
|
3129
3129
|
.suggestion-text { margin: 0; color: var(--muted); font-size: var(--text-sm); white-space: pre-wrap; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
|
|
3130
|
-
.suggestion-actions
|
|
3130
|
+
.suggestion-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
|
|
3131
|
+
.suggestion-bar, .suggestion-all { display: flex; flex-direction: column; gap: var(--space-1); margin-top: var(--space-2); }
|
|
3131
3132
|
.suggestion-all { padding-top: var(--space-2); border-top: 1px solid var(--line); }
|
|
3133
|
+
.suggestion-bar .suggestion-actions, .suggestion-all .suggestion-actions { margin-top: 0; }
|
|
3134
|
+
.suggestion-hint { margin: 0; color: var(--muted); font-size: var(--text-xs); }
|
|
3135
|
+
/* In a set, the title is the checkbox's label, so the whole line is the tap target. */
|
|
3136
|
+
.suggestion-pick { display: flex; align-items: flex-start; gap: var(--space-2); cursor: pointer; }
|
|
3137
|
+
.suggestion-pick input { flex: none; inline-size: 18px; block-size: 18px; margin: 2px 0 0; accent-color: var(--accent); }
|
|
3132
3138
|
.suggestion-status { margin: var(--space-2) 0 0; display: flex; align-items: center; gap: var(--space-2); color: var(--ok); font-size: var(--text-sm); font-weight: var(--weight-medium); }
|
|
3133
3139
|
/* Notice (NoticeCard.tsx): a message the agent sent with `bivy notify`.
|
|
3134
3140
|
Shell and tone from .card, the urgent marker from .badge. */
|
package/dist/server.js
CHANGED
|
@@ -74,7 +74,7 @@ import { exportProviderAuth, exportAccountApiKeys, exportAccountOAuthCredentials
|
|
|
74
74
|
import { listProviders } from "./runtime/provider-catalog.js";
|
|
75
75
|
import { exportLocalModels, importLocalModels } from "./runtime/local-model-store.js";
|
|
76
76
|
import { sessionLikeFields } from "./session/start-like.js";
|
|
77
|
-
import { MAX_SUGGESTION_TEXT, MAX_SUGGESTION_TITLE, isTaskSuggestion } from "./session/suggestions.js";
|
|
77
|
+
import { MAX_SUGGESTION_TEXT, MAX_SUGGESTION_TITLE, SUGGESTION_RUNS, isTaskSuggestion } from "./session/suggestions.js";
|
|
78
78
|
import { BIVY_AGENT_NOTE, mergeSyncedAgentInstructions, readAgentInstructions, sessionInstructions, writeAgentInstructions, MAX_AGENT_INSTRUCTIONS_BYTES } from "./agent-instructions.js";
|
|
79
79
|
import { execEphemeralRequest } from "./ephemeral-exec.js";
|
|
80
80
|
import { ApprovalManager } from "./approval.js";
|
|
@@ -11666,16 +11666,18 @@ app.get("/api/session/:id/automations/apply/:proposalId", (req, res) => {
|
|
|
11666
11666
|
res.json(proposal);
|
|
11667
11667
|
});
|
|
11668
11668
|
// `bivy suggest "<task>"`: the agent proposes a task the user can start in one
|
|
11669
|
-
// tap, here or in a parallel session
|
|
11669
|
+
// tap, here, through this agent's sub-agents, or in a parallel session; `run`
|
|
11670
|
+
// is the one the agent recommends (see packages/web SuggestionCard).
|
|
11670
11671
|
app.post("/api/session/:id/suggest", (req, res) => {
|
|
11671
11672
|
const record = openSessions.get(String(req.params.id));
|
|
11672
11673
|
if (!record)
|
|
11673
11674
|
return res.status(404).json({ error: "Session not found" });
|
|
11674
11675
|
const text = typeof req.body?.text === "string" ? req.body.text.trim() : "";
|
|
11675
11676
|
const title = typeof req.body?.title === "string" && req.body.title.trim() ? req.body.title.trim() : undefined;
|
|
11676
|
-
const
|
|
11677
|
+
const run = req.body?.run || undefined;
|
|
11678
|
+
const suggestion = { id: `suggestion-${randomBytes(8).toString("hex")}`, text, ...(title ? { title } : {}), ...(run ? { run } : {}) };
|
|
11677
11679
|
if (!isTaskSuggestion(suggestion))
|
|
11678
|
-
return res.status(400).json({ error: `A suggestion needs text (up to ${MAX_SUGGESTION_TEXT} characters)
|
|
11680
|
+
return res.status(400).json({ error: `A suggestion needs text (up to ${MAX_SUGGESTION_TEXT} characters), an optional title (up to ${MAX_SUGGESTION_TITLE}) and an optional run (${SUGGESTION_RUNS.join(", ")}).` });
|
|
11679
11681
|
eventLog.appendSuggestion(record.id, { afterMessageCount: record.session.getMessages().length, suggestion });
|
|
11680
11682
|
broadcast(stampSessionEvent({ type: "session.event", sessionId: record.id, event: { type: "suggestion", id: suggestion.id, suggestion } }));
|
|
11681
11683
|
res.json({ ok: true, id: suggestion.id });
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
2
|
// Copyright (c) 2026 Petter André Sjulstad
|
|
3
|
+
/** here: this session's agent. subagents: this agent, through its own sub-agents. new: a session per task. */
|
|
4
|
+
export const SUGGESTION_RUNS = ["here", "subagents", "new"];
|
|
5
|
+
const isRun = (value) => value === undefined || SUGGESTION_RUNS.includes(value);
|
|
3
6
|
export const SUGGESTION_BLOCK = "bivy_suggestion";
|
|
4
7
|
export const MAX_SUGGESTION_TEXT = 4000;
|
|
5
8
|
export const MAX_SUGGESTION_TITLE = 120;
|
|
@@ -9,5 +12,6 @@ export function isTaskSuggestion(value) {
|
|
|
9
12
|
const s = value;
|
|
10
13
|
return typeof s.id === "string" && s.id.length > 0
|
|
11
14
|
&& typeof s.text === "string" && s.text.length > 0 && s.text.length <= MAX_SUGGESTION_TEXT
|
|
12
|
-
&& (s.title === undefined || (typeof s.title === "string" && s.title.length <= MAX_SUGGESTION_TITLE))
|
|
15
|
+
&& (s.title === undefined || (typeof s.title === "string" && s.title.length <= MAX_SUGGESTION_TITLE))
|
|
16
|
+
&& isRun(s.run);
|
|
13
17
|
}
|
package/package.json
CHANGED