@hachej/boring-agent 0.1.40 → 0.1.41
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/dist/front/index.d.ts +3 -1
- package/dist/front/index.js +9 -3
- package/dist/server/index.js +68 -13
- package/package.json +2 -2
package/dist/front/index.d.ts
CHANGED
|
@@ -535,6 +535,8 @@ interface PiChatPanelProps {
|
|
|
535
535
|
serverResourcesEnabled?: boolean;
|
|
536
536
|
mentionedFiles?: string[] | (() => string[]);
|
|
537
537
|
commands?: SlashCommand[];
|
|
538
|
+
/** Built-in slash command names to omit from the composer command registry. */
|
|
539
|
+
excludeBuiltinCommands?: string[];
|
|
538
540
|
toolRenderers?: ToolRendererOverrides;
|
|
539
541
|
createRemoteSession?: (options: RemotePiSessionOptions) => RemotePiSession;
|
|
540
542
|
remoteSessionOptions?: UsePiSessionsOptions['remoteSessionOptions'];
|
|
@@ -552,7 +554,7 @@ interface PiChatPanelProps {
|
|
|
552
554
|
onComposerStop?: () => void;
|
|
553
555
|
onComposerBlockerAction?: (blocker: ComposerBlocker, action: string) => void;
|
|
554
556
|
}
|
|
555
|
-
declare function PiChatPanel({ sessionId, extraCommands, apiBaseUrl, workspaceId, storageScope, requestHeaders, storage, fetch, className, chrome, debug, showSessions, hotReloadEnabled, suggestions, emptyState, emptyPlacement, composerPlaceholder, initialDraft, autoSubmitInitialDraft, onDraftRestored, onAutoSubmitInitialDraftAccepted, onAutoSubmitInitialDraftSettled, model, defaultModel, availableModels, thinkingLevel, thinkingControl, serverResourcesEnabled, mentionedFiles, commands, toolRenderers, createRemoteSession, remoteSessionOptions, hydrateMessages, workspaceWarmupStatus, onSessionReset, onBeforeSubmit, onReloadAgentPlugins, onCommandResult, onComposerWarning, onMentionedFilesConsumed, onData, onOpenArtifact, composerBlockers, onComposerStop, onComposerBlockerAction, }: PiChatPanelProps): react_jsx_runtime.JSX.Element;
|
|
557
|
+
declare function PiChatPanel({ sessionId, extraCommands, apiBaseUrl, workspaceId, storageScope, requestHeaders, storage, fetch, className, chrome, debug, showSessions, hotReloadEnabled, suggestions, emptyState, emptyPlacement, composerPlaceholder, initialDraft, autoSubmitInitialDraft, onDraftRestored, onAutoSubmitInitialDraftAccepted, onAutoSubmitInitialDraftSettled, model, defaultModel, availableModels, thinkingLevel, thinkingControl, serverResourcesEnabled, mentionedFiles, commands, excludeBuiltinCommands, toolRenderers, createRemoteSession, remoteSessionOptions, hydrateMessages, workspaceWarmupStatus, onSessionReset, onBeforeSubmit, onReloadAgentPlugins, onCommandResult, onComposerWarning, onMentionedFilesConsumed, onData, onOpenArtifact, composerBlockers, onComposerStop, onComposerBlockerAction, }: PiChatPanelProps): react_jsx_runtime.JSX.Element;
|
|
556
558
|
|
|
557
559
|
interface DebugDrawerProps {
|
|
558
560
|
apiBaseUrl?: string;
|
package/dist/front/index.js
CHANGED
|
@@ -8534,7 +8534,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
|
|
|
8534
8534
|
autoComplete: "off"
|
|
8535
8535
|
}
|
|
8536
8536
|
),
|
|
8537
|
-
|
|
8537
|
+
/* @__PURE__ */ jsx30("div", { className: "flex flex-wrap gap-1 border-b border-border/50 px-2 py-1.5", role: "tablist", "aria-label": "Filter by plugin", children: [ALL_PLUGINS, ...groups].map((g) => {
|
|
8538
8538
|
const selected = plugin === g;
|
|
8539
8539
|
return /* @__PURE__ */ jsx30(
|
|
8540
8540
|
"button",
|
|
@@ -9173,6 +9173,7 @@ function errorMessage2(error, fallback) {
|
|
|
9173
9173
|
import { jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
9174
9174
|
var DebugDrawer2 = lazy(() => import("../DebugDrawer-2PUDZ2RL.js").then((m) => ({ default: m.DebugDrawer })));
|
|
9175
9175
|
var EMPTY_COMMANDS = [];
|
|
9176
|
+
var EMPTY_COMMAND_NAMES = [];
|
|
9176
9177
|
var EMPTY_BLOCKERS = [];
|
|
9177
9178
|
function PiChatPanel({
|
|
9178
9179
|
sessionId,
|
|
@@ -9205,6 +9206,7 @@ function PiChatPanel({
|
|
|
9205
9206
|
serverResourcesEnabled = true,
|
|
9206
9207
|
mentionedFiles,
|
|
9207
9208
|
commands = EMPTY_COMMANDS,
|
|
9209
|
+
excludeBuiltinCommands = EMPTY_COMMAND_NAMES,
|
|
9208
9210
|
toolRenderers,
|
|
9209
9211
|
createRemoteSession,
|
|
9210
9212
|
remoteSessionOptions,
|
|
@@ -9363,12 +9365,16 @@ function PiChatPanel({
|
|
|
9363
9365
|
setLocalSubmittedSessionId(void 0);
|
|
9364
9366
|
}, []);
|
|
9365
9367
|
const registry = useMemo12(() => {
|
|
9366
|
-
const
|
|
9368
|
+
const excludedBuiltins = new Set(excludeBuiltinCommands);
|
|
9369
|
+
const effectiveBuiltins = builtinCommands.filter((command) => {
|
|
9370
|
+
if (!hotReloadEnabled && command.name === "reload") return false;
|
|
9371
|
+
return !excludedBuiltins.has(command.name);
|
|
9372
|
+
});
|
|
9367
9373
|
const next = createCommandRegistry(effectiveBuiltins);
|
|
9368
9374
|
for (const command of extraCommands ?? []) next.register(command);
|
|
9369
9375
|
for (const command of commands) next.register(command);
|
|
9370
9376
|
return next;
|
|
9371
|
-
}, [apiBaseUrl, commands, extraCommands, hotReloadEnabled, normalizedRequestHeaders, serverResourcesEnabled, serverSkillsRefreshKey, storageScope]);
|
|
9377
|
+
}, [apiBaseUrl, commands, excludeBuiltinCommands, extraCommands, hotReloadEnabled, normalizedRequestHeaders, serverResourcesEnabled, serverSkillsRefreshKey, storageScope]);
|
|
9372
9378
|
const commandsStamp = useServerCommands({
|
|
9373
9379
|
registry,
|
|
9374
9380
|
requestHeaders: normalizedRequestHeaders,
|
package/dist/server/index.js
CHANGED
|
@@ -87,17 +87,18 @@ function getBoringAgentRuntimeEnv(paths, adapterCacheRoot = paths.cache) {
|
|
|
87
87
|
|
|
88
88
|
// src/server/sandbox/workspacePythonEnv.ts
|
|
89
89
|
function withWorkspacePythonEnv(opts) {
|
|
90
|
-
const { workspaceRoot, env, sandboxRoot } = opts;
|
|
90
|
+
const { workspaceRoot, env, sandboxRoot, preserveHostHome } = opts;
|
|
91
91
|
const runtimeRoot = sandboxRoot ?? workspaceRoot;
|
|
92
92
|
const paths = getBoringAgentRuntimePaths(runtimeRoot);
|
|
93
93
|
const baseEnv = env ?? getEnvSnapshot();
|
|
94
94
|
const pathParts = getBoringAgentPathEntries(paths);
|
|
95
95
|
const existingPath = baseEnv.PATH;
|
|
96
96
|
if (existingPath) pathParts.push(existingPath);
|
|
97
|
+
const home = preserveHostHome ? baseEnv.HOME : runtimeRoot;
|
|
97
98
|
return {
|
|
98
99
|
...baseEnv,
|
|
99
100
|
PATH: pathParts.join(":"),
|
|
100
|
-
HOME:
|
|
101
|
+
HOME: home,
|
|
101
102
|
VIRTUAL_ENV: paths.venv,
|
|
102
103
|
PYTHONHOME: void 0,
|
|
103
104
|
BORING_AGENT_WORKSPACE_ROOT: runtimeRoot
|
|
@@ -168,7 +169,7 @@ function createDirectSandbox(opts = {}) {
|
|
|
168
169
|
return await new Promise((resolve11, reject) => {
|
|
169
170
|
const child = spawn(cmd, {
|
|
170
171
|
cwd,
|
|
171
|
-
env: withWorkspacePythonEnv({ workspaceRoot, env: opts2?.env }),
|
|
172
|
+
env: withWorkspacePythonEnv({ workspaceRoot, env: opts2?.env, preserveHostHome: true }),
|
|
172
173
|
shell: true,
|
|
173
174
|
windowsHide: true,
|
|
174
175
|
detached: process.platform !== "win32"
|
|
@@ -427,9 +428,8 @@ async function ensureWritableWorkspacePath(workspaceRoot, relPath) {
|
|
|
427
428
|
return absPath;
|
|
428
429
|
}
|
|
429
430
|
|
|
430
|
-
// src/server/workspace/
|
|
431
|
-
var
|
|
432
|
-
var DEFAULT_WATCH_IGNORES = [
|
|
431
|
+
// src/server/workspace/ignore.ts
|
|
432
|
+
var DEFAULT_IGNORED_DIR_NAMES = [
|
|
433
433
|
"node_modules",
|
|
434
434
|
".git",
|
|
435
435
|
".DS_Store",
|
|
@@ -441,12 +441,17 @@ var DEFAULT_WATCH_IGNORES = [
|
|
|
441
441
|
".turbo",
|
|
442
442
|
"test-results"
|
|
443
443
|
];
|
|
444
|
+
var IGNORED_SET = new Set(DEFAULT_IGNORED_DIR_NAMES);
|
|
445
|
+
function isIgnoredDirName(name) {
|
|
446
|
+
return IGNORED_SET.has(name) || name.endsWith(".tsbuildinfo");
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/server/workspace/createNodeWorkspace.ts
|
|
450
|
+
var EPERM_CODE = "EPERM";
|
|
444
451
|
function shouldIgnoreWatchPath(root, path4) {
|
|
445
452
|
const relPath = relative2(root, path4);
|
|
446
453
|
const parts = relPath.split(sep);
|
|
447
|
-
return parts.some(
|
|
448
|
-
(part) => DEFAULT_WATCH_IGNORES.includes(part) || part.endsWith(".tsbuildinfo")
|
|
449
|
-
);
|
|
454
|
+
return parts.some((part) => isIgnoredDirName(part));
|
|
450
455
|
}
|
|
451
456
|
function createNodeWatcher(root) {
|
|
452
457
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -6435,12 +6440,28 @@ function createPiCodingAgentHarness(opts) {
|
|
|
6435
6440
|
);
|
|
6436
6441
|
};
|
|
6437
6442
|
refreshEffectiveResources();
|
|
6443
|
+
const piSessionCreations = /* @__PURE__ */ new Map();
|
|
6438
6444
|
async function getOrCreatePiSession(sessionId, input, ctx) {
|
|
6439
6445
|
const existing = piSessions.get(sessionId);
|
|
6440
6446
|
if (existing) {
|
|
6441
6447
|
await applyRequestedSessionOptions(existing, input);
|
|
6442
6448
|
return existing;
|
|
6443
6449
|
}
|
|
6450
|
+
const inFlight = piSessionCreations.get(sessionId);
|
|
6451
|
+
if (inFlight) {
|
|
6452
|
+
const handle = await inFlight;
|
|
6453
|
+
await applyRequestedSessionOptions(handle, input);
|
|
6454
|
+
return handle;
|
|
6455
|
+
}
|
|
6456
|
+
const creation = createPiSession(sessionId, input, ctx);
|
|
6457
|
+
piSessionCreations.set(sessionId, creation);
|
|
6458
|
+
try {
|
|
6459
|
+
return await creation;
|
|
6460
|
+
} finally {
|
|
6461
|
+
if (piSessionCreations.get(sessionId) === creation) piSessionCreations.delete(sessionId);
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
async function createPiSession(sessionId, input, ctx) {
|
|
6444
6465
|
const authStorage = AuthStorage.create();
|
|
6445
6466
|
const modelRegistry = ModelRegistry.create(authStorage);
|
|
6446
6467
|
registerConfiguredModelProviders(modelRegistry);
|
|
@@ -7588,7 +7609,8 @@ function directSpawnHook(workspaceRoot, runtime) {
|
|
|
7588
7609
|
...context,
|
|
7589
7610
|
env: withWorkspacePythonEnv({
|
|
7590
7611
|
workspaceRoot,
|
|
7591
|
-
env: mergeRuntimeEnv(runtime, context.env)
|
|
7612
|
+
env: mergeRuntimeEnv(runtime, context.env),
|
|
7613
|
+
preserveHostHome: true
|
|
7592
7614
|
})
|
|
7593
7615
|
});
|
|
7594
7616
|
}
|
|
@@ -7990,7 +8012,7 @@ async function listTree(workspace, dir, recursive) {
|
|
|
7990
8012
|
if (entries.length >= MAX_ENTRIES) break;
|
|
7991
8013
|
const entryPath = joinPath2(batch.parentDir, e.name);
|
|
7992
8014
|
entries.push({ name: e.name, kind: e.kind, path: entryPath });
|
|
7993
|
-
if (recursive && e.kind === "dir" && batch.depth < MAX_DEPTH) {
|
|
8015
|
+
if (recursive && e.kind === "dir" && batch.depth < MAX_DEPTH && !isIgnoredDirName(e.name)) {
|
|
7994
8016
|
try {
|
|
7995
8017
|
const subEntries = await workspace.readdir(entryPath);
|
|
7996
8018
|
queue.push({ parentDir: entryPath, items: subEntries, depth: batch.depth + 1 });
|
|
@@ -10089,6 +10111,11 @@ var HarnessPiChatService = class {
|
|
|
10089
10111
|
sessionStore;
|
|
10090
10112
|
workdir;
|
|
10091
10113
|
channels = /* @__PURE__ */ new Map();
|
|
10114
|
+
// Single-flight guard so concurrent cold callers (e.g. two browser tabs each
|
|
10115
|
+
// opening /events while the session is still being created) converge on one
|
|
10116
|
+
// LiveSessionChannel instead of racing through ensureChannel and orphaning
|
|
10117
|
+
// the loser's adapter subscription.
|
|
10118
|
+
channelCreations = /* @__PURE__ */ new Map();
|
|
10092
10119
|
messageMetadata = new PiChatMessageMetadataReconciler();
|
|
10093
10120
|
activePromptRuns = /* @__PURE__ */ new Map();
|
|
10094
10121
|
syntheticPromptFailures = /* @__PURE__ */ new Map();
|
|
@@ -10348,10 +10375,38 @@ var HarnessPiChatService = class {
|
|
|
10348
10375
|
async getChannel(ctx, sessionId) {
|
|
10349
10376
|
const existing = this.channels.get(sessionId);
|
|
10350
10377
|
if (existing) return existing;
|
|
10351
|
-
|
|
10352
|
-
return this.ensureChannel(ctx, sessionId, adapter);
|
|
10378
|
+
return this.createChannelOnce(sessionId, () => this.getAdapter(ctx, sessionId, ""));
|
|
10353
10379
|
}
|
|
10354
10380
|
async ensureChannel(ctx, sessionId, adapter) {
|
|
10381
|
+
const existing = this.channels.get(sessionId);
|
|
10382
|
+
if (existing) return existing;
|
|
10383
|
+
return this.createChannelOnce(sessionId, async () => adapter);
|
|
10384
|
+
}
|
|
10385
|
+
/**
|
|
10386
|
+
* Resolve (or create) the single LiveSessionChannel for a session, coalescing
|
|
10387
|
+
* concurrent cold callers onto one in-flight creation. Without this guard two
|
|
10388
|
+
* tabs opening /events on a not-yet-live session both fall through the channel
|
|
10389
|
+
* cache miss, build separate channels + adapter subscriptions, and the second
|
|
10390
|
+
* `this.channels.set` orphans the first — so the first tab's stream silently
|
|
10391
|
+
* stops receiving events.
|
|
10392
|
+
*/
|
|
10393
|
+
async createChannelOnce(sessionId, resolveAdapter) {
|
|
10394
|
+
const inFlight = this.channelCreations.get(sessionId);
|
|
10395
|
+
if (inFlight) return inFlight;
|
|
10396
|
+
const creation = (async () => {
|
|
10397
|
+
const existing = this.channels.get(sessionId);
|
|
10398
|
+
if (existing) return existing;
|
|
10399
|
+
const adapter = await resolveAdapter();
|
|
10400
|
+
return this.buildChannel(sessionId, adapter);
|
|
10401
|
+
})();
|
|
10402
|
+
this.channelCreations.set(sessionId, creation);
|
|
10403
|
+
try {
|
|
10404
|
+
return await creation;
|
|
10405
|
+
} finally {
|
|
10406
|
+
if (this.channelCreations.get(sessionId) === creation) this.channelCreations.delete(sessionId);
|
|
10407
|
+
}
|
|
10408
|
+
}
|
|
10409
|
+
buildChannel(sessionId, adapter) {
|
|
10355
10410
|
const existing = this.channels.get(sessionId);
|
|
10356
10411
|
if (existing) return existing;
|
|
10357
10412
|
const buffer = new PiChatReplayBuffer();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.41",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Pane-embeddable coding agent. Ships direct/local/vercel-sandbox execution modes behind one interface.",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"use-stick-to-bottom": "^1.1.3",
|
|
75
75
|
"yaml": "^2.8.3",
|
|
76
76
|
"zod": "^3.25.76",
|
|
77
|
-
"@hachej/boring-ui-kit": "0.1.
|
|
77
|
+
"@hachej/boring-ui-kit": "0.1.41"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@antithesishq/bombadil": "0.5.0",
|