@hachej/boring-agent 0.1.96 → 0.1.98
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.
|
@@ -6519,6 +6519,7 @@ var HarnessPiChatService = class {
|
|
|
6519
6519
|
if (persisted && !channel && !this.harnessMayHaveLiveSession(ctx, sessionId)) return persisted;
|
|
6520
6520
|
}
|
|
6521
6521
|
const adapter = await this.getAdapter(ctx, sessionId, "");
|
|
6522
|
+
await this.ensureChannel(ctx, sessionId, adapter);
|
|
6522
6523
|
if (this.canRefreshFromPersistedState(sessionKey, adapter)) {
|
|
6523
6524
|
const persisted = await this.readPersistedState(ctx, sessionId);
|
|
6524
6525
|
if (persisted && this.canRefreshFromPersistedState(sessionKey, adapter) && !this.persistedStateDropsLiveMessages(persisted, adapter, sessionId)) {
|
|
@@ -12158,6 +12159,7 @@ function createPiResourceDigestInput(input) {
|
|
|
12158
12159
|
additionalSkillPaths: uniqueStrings(input.resourceSets.flatMap((set) => set.additionalSkillPaths ?? [])),
|
|
12159
12160
|
packages: compactPiPackages(input.resourceSets.flatMap((set) => set.packages ?? [])),
|
|
12160
12161
|
extensionPaths: uniqueStrings(input.resourceSets.flatMap((set) => set.extensionPaths ?? [])),
|
|
12162
|
+
allowInternalSymlinks: input.allowInternalSymlinks,
|
|
12161
12163
|
authorizedRoots: uniqueStrings([
|
|
12162
12164
|
piCwd,
|
|
12163
12165
|
piAgentDir,
|
|
@@ -12181,7 +12183,14 @@ async function digestPiResourceInputs(input) {
|
|
|
12181
12183
|
throw stableError(ErrorCode.enum.CONFIG_INVALID, 400, "Pi resource digest requires at least one independently authorized root");
|
|
12182
12184
|
}
|
|
12183
12185
|
const limits = normalizeLimits(input.limits);
|
|
12184
|
-
const state = {
|
|
12186
|
+
const state = {
|
|
12187
|
+
hash,
|
|
12188
|
+
limits,
|
|
12189
|
+
authorizedRoots,
|
|
12190
|
+
allowInternalSymlinks: input.allowInternalSymlinks ?? false,
|
|
12191
|
+
nodes: 0,
|
|
12192
|
+
bytes: 0
|
|
12193
|
+
};
|
|
12185
12194
|
frameString(hash, "format", FORMAT_VERSION);
|
|
12186
12195
|
frameString(hash, "pi-cwd", piCwd);
|
|
12187
12196
|
frameString(hash, "project-settings-dir", projectSettingsDir);
|
|
@@ -12254,7 +12263,7 @@ function packageDescriptor(source) {
|
|
|
12254
12263
|
async function hashResourceCollection(state, baseDir, kind, paths, digestContainingArtifact) {
|
|
12255
12264
|
for (const path of [...new Set(paths)].sort()) {
|
|
12256
12265
|
const absolutePath = resolvePiPath(path, baseDir);
|
|
12257
|
-
await assertContainedWithoutSymlinks(absolutePath, state.authorizedRoots);
|
|
12266
|
+
await assertContainedWithoutSymlinks(absolutePath, state.authorizedRoots, state.allowInternalSymlinks);
|
|
12258
12267
|
try {
|
|
12259
12268
|
await lstat4(absolutePath);
|
|
12260
12269
|
} catch (error) {
|
|
@@ -12337,7 +12346,7 @@ async function findContainingArtifactRoot(path, authorizedRoots) {
|
|
|
12337
12346
|
}
|
|
12338
12347
|
async function hashLocalResource(state, path, logicalPath, depth) {
|
|
12339
12348
|
const absolutePath = resolve8(path);
|
|
12340
|
-
await assertContainedWithoutSymlinks(absolutePath, state.authorizedRoots);
|
|
12349
|
+
await assertContainedWithoutSymlinks(absolutePath, state.authorizedRoots, state.allowInternalSymlinks);
|
|
12341
12350
|
if (depth > state.limits.maxDepth) {
|
|
12342
12351
|
throw limitError(`Pi resource tree exceeds maximum depth ${state.limits.maxDepth}`);
|
|
12343
12352
|
}
|
|
@@ -12356,7 +12365,16 @@ async function hashLocalResource(state, path, logicalPath, depth) {
|
|
|
12356
12365
|
throw limitError(`Pi resource tree exceeds maximum node count ${state.limits.maxFiles}`);
|
|
12357
12366
|
}
|
|
12358
12367
|
if (stat7.isSymbolicLink()) {
|
|
12359
|
-
|
|
12368
|
+
if (!state.allowInternalSymlinks) {
|
|
12369
|
+
throw stableError(ErrorCode.enum.PATH_SYMLINK_ESCAPE, 403, `Pi resource symlinks are not allowed: ${absolutePath}`);
|
|
12370
|
+
}
|
|
12371
|
+
const target = await realpath5(absolutePath);
|
|
12372
|
+
if (!mostSpecificContainingRoot(target, state.authorizedRoots)) {
|
|
12373
|
+
throw stableError(ErrorCode.enum.PATH_SYMLINK_ESCAPE, 403, `Pi resource symlink resolves outside authorized roots: ${absolutePath}`);
|
|
12374
|
+
}
|
|
12375
|
+
frameString(state.hash, "symlink-target", target);
|
|
12376
|
+
await hashLocalResource(state, target, logicalPath, depth);
|
|
12377
|
+
return;
|
|
12360
12378
|
}
|
|
12361
12379
|
if (stat7.isDirectory()) {
|
|
12362
12380
|
frameString(state.hash, "directory", logicalPath);
|
|
@@ -12430,7 +12448,7 @@ async function hashLocalResource(state, path, logicalPath, depth) {
|
|
|
12430
12448
|
function assertSameFile(before, after, path) {
|
|
12431
12449
|
if (before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs) throw changedError(path);
|
|
12432
12450
|
}
|
|
12433
|
-
async function assertContainedWithoutSymlinks(path, roots) {
|
|
12451
|
+
async function assertContainedWithoutSymlinks(path, roots, allowInternalSymlinks = false) {
|
|
12434
12452
|
const root = mostSpecificContainingRoot(path, roots);
|
|
12435
12453
|
if (!root) {
|
|
12436
12454
|
throw stableError(ErrorCode.enum.PATH_ESCAPE, 403, `Pi resource path is outside authorized roots: ${path}`);
|
|
@@ -12443,7 +12461,13 @@ async function assertContainedWithoutSymlinks(path, roots) {
|
|
|
12443
12461
|
current = resolve8(current, segment);
|
|
12444
12462
|
try {
|
|
12445
12463
|
if ((await lstat4(current)).isSymbolicLink()) {
|
|
12446
|
-
|
|
12464
|
+
if (!allowInternalSymlinks) {
|
|
12465
|
+
throw stableError(ErrorCode.enum.PATH_SYMLINK_ESCAPE, 403, `Pi resource symlinks are not allowed: ${current}`);
|
|
12466
|
+
}
|
|
12467
|
+
const target = await realpath5(current);
|
|
12468
|
+
if (!mostSpecificContainingRoot(target, roots)) {
|
|
12469
|
+
throw stableError(ErrorCode.enum.PATH_SYMLINK_ESCAPE, 403, `Pi resource symlink resolves outside authorized roots: ${current}`);
|
|
12470
|
+
}
|
|
12447
12471
|
}
|
|
12448
12472
|
} catch (error) {
|
|
12449
12473
|
if (isMissing(error)) return;
|
package/dist/front/index.d.ts
CHANGED
|
@@ -373,6 +373,7 @@ interface RemotePiSessionOptions {
|
|
|
373
373
|
setTimeoutFn?: typeof globalThis.setTimeout;
|
|
374
374
|
clearTimeoutFn?: typeof globalThis.clearTimeout;
|
|
375
375
|
requestTimeoutMs?: number;
|
|
376
|
+
commandTimeoutMs?: number;
|
|
376
377
|
}
|
|
377
378
|
interface RemotePiSessionLargeStateWarning {
|
|
378
379
|
type: 'large-state';
|
|
@@ -419,6 +420,7 @@ declare class RemotePiSession {
|
|
|
419
420
|
private readonly setTimeoutFn;
|
|
420
421
|
private readonly clearTimeoutFn;
|
|
421
422
|
private readonly requestTimeoutMs;
|
|
423
|
+
private readonly commandTimeoutMs;
|
|
422
424
|
private generation;
|
|
423
425
|
private streamRunId;
|
|
424
426
|
private reconnectAttempt;
|
package/dist/front/index.js
CHANGED
|
@@ -4520,6 +4520,7 @@ var SUPPORTED_PROTOCOL_VERSION = 1;
|
|
|
4520
4520
|
var DEFAULT_RECONNECT_BASE_MS = 1e3;
|
|
4521
4521
|
var DEFAULT_RECONNECT_MAX_MS = 3e4;
|
|
4522
4522
|
var DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
|
|
4523
|
+
var DEFAULT_COMMAND_TIMEOUT_MS = 6e4;
|
|
4523
4524
|
var DEFAULT_LARGE_STATE_WARNING_BYTES = 5 * 1024 * 1024;
|
|
4524
4525
|
var DEFAULT_LARGE_STATE_WARNING_MESSAGES = 300;
|
|
4525
4526
|
var EVENT_TYPE_RING_LIMIT = 20;
|
|
@@ -4536,6 +4537,7 @@ var RemotePiSession = class {
|
|
|
4536
4537
|
this.setTimeoutFn = options.setTimeoutFn ?? globalThis.setTimeout;
|
|
4537
4538
|
this.clearTimeoutFn = options.clearTimeoutFn ?? globalThis.clearTimeout;
|
|
4538
4539
|
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
4540
|
+
this.commandTimeoutMs = options.commandTimeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;
|
|
4539
4541
|
this.store = createPiChatStore(createInitialPiChatState({
|
|
4540
4542
|
sessionId: options.sessionId,
|
|
4541
4543
|
workspaceId: options.workspaceId,
|
|
@@ -4552,6 +4554,7 @@ var RemotePiSession = class {
|
|
|
4552
4554
|
setTimeoutFn;
|
|
4553
4555
|
clearTimeoutFn;
|
|
4554
4556
|
requestTimeoutMs;
|
|
4557
|
+
commandTimeoutMs;
|
|
4555
4558
|
generation = 0;
|
|
4556
4559
|
streamRunId = 0;
|
|
4557
4560
|
reconnectAttempt = 0;
|
|
@@ -4877,7 +4880,7 @@ var RemotePiSession = class {
|
|
|
4877
4880
|
method: "POST",
|
|
4878
4881
|
headers: { ...headers, "Content-Type": "application/json" },
|
|
4879
4882
|
body: JSON.stringify(this.addressedCommandPayload(path, payload))
|
|
4880
|
-
});
|
|
4883
|
+
}, this.commandTimeoutMs);
|
|
4881
4884
|
if (!this.isGenerationActive(generation)) {
|
|
4882
4885
|
throw abortError("Remote Pi session disposed before command receipt.");
|
|
4883
4886
|
}
|
|
@@ -4894,21 +4897,21 @@ var RemotePiSession = class {
|
|
|
4894
4897
|
if (this.disposed) return;
|
|
4895
4898
|
this.store.dispatch({ type: "remove-optimistic-user-message", clientNonce }, { flush: true });
|
|
4896
4899
|
}
|
|
4897
|
-
async fetchJson(url, init) {
|
|
4900
|
+
async fetchJson(url, init, timeoutMs = this.requestTimeoutMs) {
|
|
4898
4901
|
const controller = new AbortController();
|
|
4899
4902
|
this.fetchControllers.add(controller);
|
|
4900
4903
|
let timedOut = false;
|
|
4901
4904
|
const timer = globalThis.setTimeout(() => {
|
|
4902
4905
|
timedOut = true;
|
|
4903
4906
|
controller.abort();
|
|
4904
|
-
},
|
|
4907
|
+
}, timeoutMs);
|
|
4905
4908
|
try {
|
|
4906
4909
|
const response = await this.fetchImpl(url, { ...init, signal: controller.signal });
|
|
4907
4910
|
const body = await safeReadJson(response);
|
|
4908
4911
|
if (!response.ok) throw new RemotePiSessionHttpError(response.status, routeErrorMessage(body, `HTTP ${response.status}`), body, routeErrorCode(body));
|
|
4909
4912
|
return body;
|
|
4910
4913
|
} catch (error) {
|
|
4911
|
-
if (timedOut) throw new Error(`Request to ${url} timed out after ${
|
|
4914
|
+
if (timedOut) throw new Error(`Request to ${url} timed out after ${timeoutMs}ms.`);
|
|
4912
4915
|
throw error;
|
|
4913
4916
|
} finally {
|
|
4914
4917
|
globalThis.clearTimeout(timer);
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1356,6 +1356,8 @@ interface PiResourceDigestInput {
|
|
|
1356
1356
|
readonly extensionPaths?: readonly string[];
|
|
1357
1357
|
/** Lexical roots explicitly authorized by the embedding Workspace/CLI. */
|
|
1358
1358
|
readonly authorizedRoots: readonly string[];
|
|
1359
|
+
/** CLI workspaces may contain repo-managed symlinks (e.g. .pi/skills/*). */
|
|
1360
|
+
readonly allowInternalSymlinks?: boolean;
|
|
1359
1361
|
readonly limits?: Partial<PiResourceDigestLimits>;
|
|
1360
1362
|
}
|
|
1361
1363
|
interface PiResourceSet {
|
|
@@ -1372,6 +1374,7 @@ declare function createPiResourceDigestInput(input: {
|
|
|
1372
1374
|
readonly noSkills?: boolean;
|
|
1373
1375
|
readonly resourceSets: readonly PiResourceSet[];
|
|
1374
1376
|
readonly authorizedRoots: readonly string[];
|
|
1377
|
+
readonly allowInternalSymlinks?: boolean;
|
|
1375
1378
|
readonly limits?: Partial<PiResourceDigestLimits>;
|
|
1376
1379
|
}): PiResourceDigestInput;
|
|
1377
1380
|
/** Bounded asynchronous content identity for the exact Pi resources considered by a reload candidate. */
|
package/dist/server/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.98",
|
|
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.",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"diff": "^9.0.0",
|
|
89
89
|
"fastify": "^5.11.2",
|
|
90
90
|
"lucide-react": "^1.27.0",
|
|
91
|
-
"motion": "^
|
|
91
|
+
"motion": "^13.0.0",
|
|
92
92
|
"nanoid": "^6.0.0",
|
|
93
93
|
"shiki": "^4.4.2",
|
|
94
94
|
"streamdown": "^2.5.0",
|
|
@@ -96,9 +96,9 @@
|
|
|
96
96
|
"use-stick-to-bottom": "^1.1.6",
|
|
97
97
|
"yaml": "^2.9.0",
|
|
98
98
|
"zod": "^3.25.76",
|
|
99
|
-
"@hachej/boring-bash": "0.1.
|
|
100
|
-
"@hachej/boring-
|
|
101
|
-
"@hachej/boring-
|
|
99
|
+
"@hachej/boring-bash": "0.1.98",
|
|
100
|
+
"@hachej/boring-sandbox": "0.1.98",
|
|
101
|
+
"@hachej/boring-ui-kit": "0.1.98"
|
|
102
102
|
},
|
|
103
103
|
"devDependencies": {
|
|
104
104
|
"@vercel/sandbox": "2.9.2",
|