@bivy/bivy 0.5.1-staging.59 → 0.5.1-staging.60
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// SPDX-License-Identifier: FSL-1.1-ALv2
|
|
2
2
|
// Copyright (c) 2026 Petter André Sjulstad
|
|
3
3
|
export { IntegrationManager } from "./manager.js";
|
|
4
|
-
export { BUILT_IN_INTEGRATIONS } from "./registry.js";
|
|
4
|
+
export { BUILT_IN_INTEGRATIONS, ATTACH_TO_CHAT_TOOL } from "./registry.js";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright (c) 2026 Petter André Sjulstad
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { IntegrationStore } from "./store.js";
|
|
5
|
-
import { BUILT_IN_INTEGRATIONS } from "./registry.js";
|
|
5
|
+
import { ATTACH_TO_CHAT_TOOL, BUILT_IN_INTEGRATIONS } from "./registry.js";
|
|
6
6
|
import { buildAuthorizeUrl, createPkce, exchangeCode, refreshToken, } from "./oauth.js";
|
|
7
7
|
import { SecretVault } from "../secrets.js";
|
|
8
8
|
/**
|
|
@@ -10,9 +10,11 @@ import { SecretVault } from "../secrets.js";
|
|
|
10
10
|
*
|
|
11
11
|
* - REST handlers in server.ts call `list / connectApiKey / startOAuth /
|
|
12
12
|
* completeOAuth / disconnect`.
|
|
13
|
-
* - `toolProvider()` exposes the connected integrations' tools
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* - `toolProvider()` exposes the connected integrations' tools — plus the
|
|
14
|
+
* always-on `attach_to_chat` tool (issue #291), when the daemon wired an
|
|
15
|
+
* `attachToChat` callback — as a runtime-agnostic ToolProvider handed to each
|
|
16
|
+
* session; the tools execute here on the daemon (where credentials live) for
|
|
17
|
+
* in-process AND remote agents alike.
|
|
16
18
|
*/
|
|
17
19
|
export class IntegrationManager {
|
|
18
20
|
store;
|
|
@@ -21,11 +23,15 @@ export class IntegrationManager {
|
|
|
21
23
|
pending = new Map();
|
|
22
24
|
/** Names of tools flagged risky (approval-gated), regardless of connection. */
|
|
23
25
|
riskyTools;
|
|
24
|
-
|
|
26
|
+
/** Backs the native `attach_to_chat` tool (see toolProvider); undefined = the
|
|
27
|
+
* tool isn't offered (e.g. a test harness that never wired one). */
|
|
28
|
+
attachToChat;
|
|
29
|
+
constructor(appDir, registry = BUILT_IN_INTEGRATIONS, attachToChat) {
|
|
25
30
|
this.store = new IntegrationStore(appDir);
|
|
26
31
|
this.secrets = new SecretVault(appDir);
|
|
27
32
|
this.registry = registry;
|
|
28
33
|
this.riskyTools = new Set(registry.flatMap((d) => d.tools.filter((t) => t.risky).map((t) => t.name)));
|
|
34
|
+
this.attachToChat = attachToChat;
|
|
29
35
|
}
|
|
30
36
|
// --- helpers ------------------------------------------------------------
|
|
31
37
|
def(id) {
|
|
@@ -236,15 +242,23 @@ export class IntegrationManager {
|
|
|
236
242
|
}
|
|
237
243
|
// --- agent-agnostic tool provider ---------------------------------------
|
|
238
244
|
/**
|
|
239
|
-
* A runtime-agnostic ToolProvider exposing every connected integration's tools
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
245
|
+
* A runtime-agnostic ToolProvider exposing every connected integration's tools,
|
|
246
|
+
* plus the always-on `attach_to_chat` tool (issue #291) when this manager was
|
|
247
|
+
* built with an `attachToChat` callback. This is the seam the daemon hands to a
|
|
248
|
+
* session (in-process OR remote) so any agent can use the tools without the
|
|
249
|
+
* IntegrationManager knowing which agent it is — the tools execute HERE, on the
|
|
250
|
+
* daemon, where the credentials/HTTP clients live. A snapshot of the connected
|
|
251
|
+
* set is taken per call (at session start), mirroring the previous per-session
|
|
252
|
+
* behavior; disconnected integrations contribute nothing, so the tool surface
|
|
253
|
+
* and system prompt stay clean.
|
|
254
|
+
*
|
|
255
|
+
* `sessionIdRef` resolves the calling session for `attach_to_chat`: the
|
|
256
|
+
* provider is built before the session it will serve exists (see
|
|
257
|
+
* AttachToChatFn's doc), so the caller passes a box and fills `.current` in
|
|
258
|
+
* once the id is known rather than a plain string. Ignored (and the tool
|
|
259
|
+
* omitted) when either it or the attachToChat callback is absent.
|
|
246
260
|
*/
|
|
247
|
-
toolProvider() {
|
|
261
|
+
toolProvider(sessionIdRef) {
|
|
248
262
|
const specs = [];
|
|
249
263
|
const executors = new Map();
|
|
250
264
|
for (const def of this.registry) {
|
|
@@ -266,6 +280,30 @@ export class IntegrationManager {
|
|
|
266
280
|
});
|
|
267
281
|
}
|
|
268
282
|
}
|
|
283
|
+
if (this.attachToChat && sessionIdRef) {
|
|
284
|
+
const attachToChat = this.attachToChat;
|
|
285
|
+
specs.push({
|
|
286
|
+
name: ATTACH_TO_CHAT_TOOL.name,
|
|
287
|
+
label: ATTACH_TO_CHAT_TOOL.label,
|
|
288
|
+
description: ATTACH_TO_CHAT_TOOL.description,
|
|
289
|
+
promptSnippet: ATTACH_TO_CHAT_TOOL.description,
|
|
290
|
+
parameters: ATTACH_TO_CHAT_TOOL.parameters,
|
|
291
|
+
});
|
|
292
|
+
executors.set(ATTACH_TO_CHAT_TOOL.name, async (params) => {
|
|
293
|
+
const sessionId = sessionIdRef.current;
|
|
294
|
+
if (!sessionId)
|
|
295
|
+
return { content: [{ type: "text", text: "Session is not ready yet — try again in a moment." }], details: {}, isError: true };
|
|
296
|
+
const p = (params ?? {});
|
|
297
|
+
const filePath = typeof p.filePath === "string" ? p.filePath.trim() : "";
|
|
298
|
+
if (!filePath)
|
|
299
|
+
return { content: [{ type: "text", text: "filePath is required" }], details: {}, isError: true };
|
|
300
|
+
const caption = typeof p.caption === "string" ? p.caption : undefined;
|
|
301
|
+
const result = attachToChat(sessionId, { filePath, caption });
|
|
302
|
+
if ("error" in result)
|
|
303
|
+
return { content: [{ type: "text", text: result.error }], details: {}, isError: true };
|
|
304
|
+
return { content: [{ type: "text", text: `Attached ${result.ref.name} (${result.ref.kind}, ${result.ref.mimeType}) to the chat.` }], details: { ref: result.ref } };
|
|
305
|
+
});
|
|
306
|
+
}
|
|
269
307
|
return {
|
|
270
308
|
list: () => specs,
|
|
271
309
|
invoke: async (toolName, _toolCallId, params, signal) => {
|
|
@@ -16,6 +16,29 @@ function toRfc822({ to, subject, body }) {
|
|
|
16
16
|
const lines = [`To: ${to}`, `Subject: ${subject}`, "Content-Type: text/plain; charset=UTF-8", "", body];
|
|
17
17
|
return Buffer.from(lines.join("\r\n")).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
18
18
|
}
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// attach_to_chat (issue #291): the native, tool-based sibling of the CLI
|
|
21
|
+
// `bivy attach <path>` / POST /api/session/:id/attach path #288 added. Unlike
|
|
22
|
+
// the integrations below it needs no auth and isn't gated on a "connected"
|
|
23
|
+
// account — every agent should always have it — so it's declared here as data
|
|
24
|
+
// (name/description/schema, matching every other tool) but wired up by
|
|
25
|
+
// IntegrationManager.toolProvider instead of BUILT_IN_INTEGRATIONS: its
|
|
26
|
+
// executor needs the calling session's id (to push the attachment into the
|
|
27
|
+
// right chat), which the `http`-only IntegrationToolDef.execute shape below
|
|
28
|
+
// has no room for.
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
export const ATTACH_TO_CHAT_TOOL = {
|
|
31
|
+
name: "attach_to_chat",
|
|
32
|
+
label: "Attach to chat",
|
|
33
|
+
description: "Push a file or image from the session workspace into the chat as an attachment, exactly like the CLI `bivy attach` " +
|
|
34
|
+
"or the composer's paperclip upload but as a direct tool call. Use this — not markdown image syntax, not describing " +
|
|
35
|
+
"where a file lives — whenever the user should see a report, screenshot, chart, or a file they asked for; they " +
|
|
36
|
+
"cannot see files you only write to disk. The path must be inside the session workspace.",
|
|
37
|
+
parameters: Type.Object({
|
|
38
|
+
filePath: Type.String({ description: "Path to the file, absolute or relative to the session workspace." }),
|
|
39
|
+
caption: Type.Optional(Type.String({ description: "Short caption shown next to the attachment in the chat." })),
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
19
42
|
export const BUILT_IN_INTEGRATIONS = [
|
|
20
43
|
{
|
|
21
44
|
id: "notion",
|
|
@@ -22,6 +22,7 @@ import fs from "node:fs";
|
|
|
22
22
|
import { depCacheEnv } from "../harness/dep-cache.js";
|
|
23
23
|
import os from "node:os";
|
|
24
24
|
import path from "node:path";
|
|
25
|
+
import { z } from "zod";
|
|
25
26
|
import { sandboxTier, claudePermissionModeFor } from "../harness/sandbox.js";
|
|
26
27
|
import { autoAttachToolImagesEnabled, PassiveImageBudget } from "../harness/tool-image-attachments.js";
|
|
27
28
|
import { anthropicCredentialPreflight, describeAnthropicError, isAnthropicAuthError } from "./anthropic-preflight.js";
|
|
@@ -73,6 +74,46 @@ export const BIVY_ATTACH_SYSTEM_PROMPT = "Sending files and images to the user:
|
|
|
73
74
|
"An image renders inline in the chat; any other file shows as a downloadable chip. The path must be inside the session " +
|
|
74
75
|
"workspace. Do NOT use markdown image syntax like  to show a local file or a URL — it will not render; always " +
|
|
75
76
|
"use `bivy attach`. Prefer this over pasting large file contents or describing where a file lives on disk.";
|
|
77
|
+
/** Name of the in-process MCP server the native attach tool is registered
|
|
78
|
+
* under (see buildAttachMcpServer) — the SDK namespaces the tool the agent
|
|
79
|
+
* sees as `mcp__<server>__<tool>`. */
|
|
80
|
+
export const BIVY_ATTACH_MCP_SERVER_NAME = "bivy";
|
|
81
|
+
/** The tool's own name, unnamespaced (see BIVY_ATTACH_MCP_SERVER_NAME). */
|
|
82
|
+
export const BIVY_ATTACH_TOOL_NAME = "attach_to_chat";
|
|
83
|
+
/**
|
|
84
|
+
* Build the in-process MCP server that exposes `attach_to_chat` as a native
|
|
85
|
+
* tool call (issue #291) — the stronger sibling of BIVY_ATTACH_SYSTEM_PROMPT's
|
|
86
|
+
* shell-out hint: the agent sees this in its actual tool list instead of having
|
|
87
|
+
* to discover a shell command from prose. Bound to one session's id so the
|
|
88
|
+
* handler always attaches into the conversation that called it, regardless of
|
|
89
|
+
* how many Claude sessions this node is running concurrently.
|
|
90
|
+
*
|
|
91
|
+
* `sdk` is the already-loaded SDK module (see loadSdk) — `tool`/
|
|
92
|
+
* createSdkMcpServer are read off it dynamically for the same reason the rest
|
|
93
|
+
* of this adapter never imports SDK values statically: the package is
|
|
94
|
+
* optional, and a static import would force every Bivy install to have it.
|
|
95
|
+
* Returns undefined if this SDK build doesn't export the MCP builder helpers
|
|
96
|
+
* (older/trimmed installs) — the caller degrades to prompt-only discoverability.
|
|
97
|
+
*/
|
|
98
|
+
function buildAttachMcpServer(sdk, sessionId, attachToChat) {
|
|
99
|
+
if (typeof sdk?.tool !== "function" || typeof sdk?.createSdkMcpServer !== "function")
|
|
100
|
+
return undefined;
|
|
101
|
+
const attachTool = sdk.tool(BIVY_ATTACH_TOOL_NAME, "Push a file or image from the session workspace into the chat as an attachment, exactly like the CLI `bivy attach` " +
|
|
102
|
+
"or the composer's paperclip upload but as a direct tool call. Use this — not markdown image syntax, not describing " +
|
|
103
|
+
"where a file lives — whenever the user should see a report, screenshot, chart, or a file they asked for; they " +
|
|
104
|
+
"cannot see files you only write to disk. The path must be inside the session workspace.", {
|
|
105
|
+
filePath: z.string().describe("Path to the file, absolute or relative to the session workspace."),
|
|
106
|
+
caption: z.string().optional().describe("Short caption shown next to the attachment in the chat."),
|
|
107
|
+
}, async (args) => {
|
|
108
|
+
const result = attachToChat(sessionId, { filePath: args.filePath, caption: args.caption });
|
|
109
|
+
if ("error" in result)
|
|
110
|
+
return { content: [{ type: "text", text: result.error }], isError: true };
|
|
111
|
+
return {
|
|
112
|
+
content: [{ type: "text", text: `Attached ${result.ref.name} (${result.ref.kind}, ${result.ref.mimeType}) to the chat.` }],
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
return sdk.createSdkMcpServer({ name: BIVY_ATTACH_MCP_SERVER_NAME, tools: [attachTool] });
|
|
116
|
+
}
|
|
76
117
|
export function claudeRuntimeFromEnv() {
|
|
77
118
|
return {
|
|
78
119
|
defaultModel: process.env.BIVY_CLAUDE_MODEL?.trim() || undefined,
|
|
@@ -737,6 +778,9 @@ class ClaudeSession {
|
|
|
737
778
|
// Keep the default Claude Code prompt, appending the note that teaches the
|
|
738
779
|
// agent how to send a file to the user (`bivy attach`) — otherwise the
|
|
739
780
|
// capability is undiscoverable and "send me X as an attachment" fails.
|
|
781
|
+
// Kept even when the native tool below is also registered: it's a cheap,
|
|
782
|
+
// harmless fallback for a shell/subprocess the agent spawns that can't
|
|
783
|
+
// reach the in-process MCP tool directly.
|
|
740
784
|
systemPrompt: { type: "preset", preset: "claude_code", append: BIVY_ATTACH_SYSTEM_PROMPT },
|
|
741
785
|
};
|
|
742
786
|
if (resumeId)
|
|
@@ -745,6 +789,16 @@ class ClaudeSession {
|
|
|
745
789
|
options.sessionId = this.id;
|
|
746
790
|
if (this.desiredModel)
|
|
747
791
|
options.model = this.desiredModel;
|
|
792
|
+
// Native attach_to_chat tool (issue #291) — the stronger, tool-based sibling
|
|
793
|
+
// of the system-prompt hint above. Wired only when the daemon handed us a
|
|
794
|
+
// callback (see ClaudeCodeRuntimeOptions.attachToChat); absent in a few
|
|
795
|
+
// deliberately minimal test harnesses, and gracefully degrades to the prompt
|
|
796
|
+
// hint alone if this SDK build lacks the MCP builder helpers.
|
|
797
|
+
if (this.runtimeOptions.attachToChat) {
|
|
798
|
+
const attachServer = buildAttachMcpServer(sdk, this.id, this.runtimeOptions.attachToChat);
|
|
799
|
+
if (attachServer)
|
|
800
|
+
options.mcpServers = { [BIVY_ATTACH_MCP_SERVER_NAME]: attachServer };
|
|
801
|
+
}
|
|
748
802
|
const q = sdk.query({ prompt: this.input, options });
|
|
749
803
|
this.query = q;
|
|
750
804
|
void this.consume(q);
|
package/dist/runtime/index.js
CHANGED
|
@@ -1457,7 +1457,7 @@ export function makeRuntime(options) {
|
|
|
1457
1457
|
case "claude-code-sdk":
|
|
1458
1458
|
// Share the node's provider logins (the shared vault) so the user doesn't
|
|
1459
1459
|
// re-auth Anthropic for this agent.
|
|
1460
|
-
return new ClaudeCodeRuntime({ ...claudeRuntimeFromEnv(), credentials: createCredentialStore(options.credsDir), sandbox: options.sandbox });
|
|
1460
|
+
return new ClaudeCodeRuntime({ ...claudeRuntimeFromEnv(), credentials: createCredentialStore(options.credsDir), sandbox: options.sandbox, attachToChat: options.attachToChat });
|
|
1461
1461
|
default:
|
|
1462
1462
|
// Every CLI agent in CLI_AGENT_SPECS is dispatched here as data — no per-id
|
|
1463
1463
|
// case to maintain. Anything that isn't a known CLI agent throws below.
|
package/dist/server.js
CHANGED
|
@@ -484,7 +484,7 @@ function resolveApproval(id, approved) {
|
|
|
484
484
|
}
|
|
485
485
|
return ok;
|
|
486
486
|
}
|
|
487
|
-
const integrations = new IntegrationManager(appDir);
|
|
487
|
+
const integrations = new IntegrationManager(appDir, undefined, attachToChatForSession);
|
|
488
488
|
const terminals = new TerminalManager();
|
|
489
489
|
// Per-session agents: a node holds one AgentRuntime instance *per agent id*,
|
|
490
490
|
// built lazily and cached, instead of a single global runtime. `defaultRuntimeId`
|
|
@@ -492,7 +492,7 @@ const terminals = new TerminalManager();
|
|
|
492
492
|
// be swapped under a live conversation, so the agent is chosen at session creation
|
|
493
493
|
// and fixed for that session's life; switching agents in the UI starts a new one.
|
|
494
494
|
let defaultRuntimeId = (process.env.BIVY_RUNTIME ?? "pi").toLowerCase();
|
|
495
|
-
const runtimeHost = new RuntimeHost({ credsDir, piDir, sessionsDir });
|
|
495
|
+
const runtimeHost = new RuntimeHost({ credsDir, piDir, sessionsDir, attachToChat: attachToChatForSession });
|
|
496
496
|
// In-session model reroute (docs/rulesets.md). Opt-in: set
|
|
497
497
|
// BIVY_SESSION_MODEL_FALLBACK to a comma-separated model list and a session that
|
|
498
498
|
// hits an exhausted-credits / rate-limit turn error swaps down the list (via the
|
|
@@ -1129,6 +1129,22 @@ function handlePassiveToolImage(record, event) {
|
|
|
1129
1129
|
console.warn("[attachments] failed to store a passively-surfaced tool image:", result.error);
|
|
1130
1130
|
}
|
|
1131
1131
|
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Session-id-keyed wrapper around attachToChat, handed to runtime adapters as
|
|
1134
|
+
* the `attachToChat` callback that backs each agent's native "attach to chat"
|
|
1135
|
+
* tool surface (Claude's SDK tool, Pi's ToolProvider tool — issue #291). Those
|
|
1136
|
+
* tools are wired at runtime/tool-provider construction time, before the
|
|
1137
|
+
* specific session that will run them exists — a per-session circular
|
|
1138
|
+
* dependency (build the tools -> need the session -> need the tools) — so the
|
|
1139
|
+
* callback takes a session id and resolves the live record from openSessions
|
|
1140
|
+
* when it actually fires, exactly like the HTTP endpoint below does by path.
|
|
1141
|
+
*/
|
|
1142
|
+
function attachToChatForSession(sessionId, opts) {
|
|
1143
|
+
const record = openSessions.get(sessionId);
|
|
1144
|
+
if (!record)
|
|
1145
|
+
return { error: "Session not found" };
|
|
1146
|
+
return attachToChat(record, opts);
|
|
1147
|
+
}
|
|
1132
1148
|
function approvalModeFrom(value) {
|
|
1133
1149
|
return value === "never" || value === "risky" || value === "always" || value === "autonomous" ? value : undefined;
|
|
1134
1150
|
}
|
|
@@ -6825,7 +6841,9 @@ async function refreshRecordAfterTui(record) {
|
|
|
6825
6841
|
record.unsubscribe = undefined;
|
|
6826
6842
|
const rt = await ensureRuntimeAvailable(record.runtimeId);
|
|
6827
6843
|
const workspace = record.worktree?.path || oldSession.cwd || record.workspace;
|
|
6828
|
-
|
|
6844
|
+
// Refreshing an EXISTING record: its id is already known, so attach_to_chat
|
|
6845
|
+
// (see toolProvider's SessionIdRef doc) can be wired live, not deferred.
|
|
6846
|
+
const runtimeSessionOptions = { workspace, toolProvider: integrations.toolProvider({ current: record.id }), ...(rt.capabilities.toolInterception ? { toolInterceptor: guardianInterceptor } : {}) };
|
|
6829
6847
|
const { session, warning } = await runtimeHost.openSession(rt, { ...runtimeSessionOptions, sessionFile: record.sessionFile });
|
|
6830
6848
|
record.session = session;
|
|
6831
6849
|
record.sessionFile = session.sessionFile ?? record.sessionFile;
|
|
@@ -7340,7 +7358,12 @@ async function createSession(workspace = defaultWorkspace, sessionFile, opts = {
|
|
|
7340
7358
|
throw new Error(`Refusing to start a session in the shared clone root ${runtimeWorkspace} without an isolated worktree — ` +
|
|
7341
7359
|
`this would collide with concurrent sessions on the same repo. Retry; if it persists the checkout may be busy.`);
|
|
7342
7360
|
}
|
|
7343
|
-
|
|
7361
|
+
// A brand-new session's id isn't known until runtimeHost.{create,open}Session
|
|
7362
|
+
// resolves below, but the ToolProvider (and its attach_to_chat tool) must be
|
|
7363
|
+
// built now, up front — so hand it this box instead of a session id and fill
|
|
7364
|
+
// `.current` in the moment `sessionId` is (see toolProvider's SessionIdRef doc).
|
|
7365
|
+
const attachSessionIdRef = {};
|
|
7366
|
+
const runtimeSessionOptions = { workspace: runtimeWorkspace, toolProvider: integrations.toolProvider(attachSessionIdRef), ...(rt.capabilities.toolInterception ? { toolInterceptor: guardianInterceptor } : {}) };
|
|
7344
7367
|
// Stage 2/3: prefer re-attaching to a still-live remote session — routed to its
|
|
7345
7368
|
// OWN agent service — over re-opening a fresh copy from disk. Falls back to
|
|
7346
7369
|
// open/create when nothing live is there.
|
|
@@ -7358,6 +7381,10 @@ async function createSession(workspace = defaultWorkspace, sessionFile, opts = {
|
|
|
7358
7381
|
? await runtimeHost.openSession(rt, { ...runtimeSessionOptions, sessionFile: requestedSessionFile })
|
|
7359
7382
|
: await runtimeHost.createSession(rt, runtimeSessionOptions));
|
|
7360
7383
|
const sessionId = session.id;
|
|
7384
|
+
// Now that it's known, unblock any attach_to_chat call this session's agent
|
|
7385
|
+
// makes (see attachSessionIdRef above) — set synchronously, well before any
|
|
7386
|
+
// prompt (and so any tool call) can reach this session.
|
|
7387
|
+
attachSessionIdRef.current = sessionId;
|
|
7361
7388
|
// Resuming an existing session: restore Bivy's canonical name onto the runtime
|
|
7362
7389
|
// session when the runtime didn't itself (the Claude Code adapter resumes by id
|
|
7363
7390
|
// and starts nameless). Without this getName() is undefined, so opening a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bivy/bivy",
|
|
3
|
-
"version": "0.5.1-staging.
|
|
3
|
+
"version": "0.5.1-staging.60",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "FSL-1.1-ALv2",
|
|
6
6
|
"description": "Run coding agents on machines you own. Source-available, self-hostable agent workspace.",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"express": "^5.2.1",
|
|
38
38
|
"node-pty": "^1.1.0",
|
|
39
39
|
"typebox": "^1.3.6",
|
|
40
|
-
"ws": "^8.21.1"
|
|
40
|
+
"ws": "^8.21.1",
|
|
41
|
+
"zod": "^4.0.0"
|
|
41
42
|
},
|
|
42
43
|
"overrides": {
|
|
43
44
|
"@hono/node-server": "2.0.12",
|