@hachej/boring-agent 0.1.74 → 0.1.76
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/{chunk-AQVEZYLH.js → chunk-FRFCVP3X.js} +52 -13
- package/dist/{chunk-XZKU7FBV.js → chunk-M4AY4VBI.js} +1 -0
- package/dist/{chunk-FDGWCMJD.js → chunk-OABGJVSA.js} +3 -2
- package/dist/{chunk-4LXA7OOV.js → chunk-PMREGRGD.js} +1 -1
- package/dist/{chunk-WSQ5QNIY.js → chunk-ZUEITFIJ.js} +12 -1
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +8 -4
- package/dist/{createHarness-B3MYGFVB.js → createHarness-HQ3RHXTF.js} +2 -2
- package/dist/front/index.d.ts +2 -2
- package/dist/front/index.js +2 -2
- package/dist/{harness-Dwxh6USt.d.ts → harness-BrX6CdMT.d.ts} +10 -2
- package/dist/{piChatCommand-CSepU1NX.d.ts → piChatCommand-5UO2F_gv.d.ts} +1 -1
- package/dist/server/index.d.ts +5 -3
- package/dist/server/index.js +556 -279
- package/dist/{session-DKMQNtNG.d.ts → session-DTV7L5dc.d.ts} +13 -13
- package/dist/shared/index.d.ts +23 -23
- package/dist/shared/index.js +7 -3
- package/docs/ERROR_CODES.md +1 -0
- package/docs/runtime.md +12 -0
- package/package.json +2 -2
package/dist/server/index.js
CHANGED
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
createAgent,
|
|
8
8
|
createAgentRuntimeBridge,
|
|
9
9
|
normalizeMeteringUsage
|
|
10
|
-
} from "../chunk-
|
|
11
|
-
import "../chunk-
|
|
10
|
+
} from "../chunk-FRFCVP3X.js";
|
|
11
|
+
import "../chunk-ZUEITFIJ.js";
|
|
12
12
|
import {
|
|
13
13
|
FollowUpPayloadSchema,
|
|
14
14
|
InterruptPayloadSchema,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
PromptPayloadSchema,
|
|
17
17
|
QueueClearPayloadSchema,
|
|
18
18
|
StopPayloadSchema
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-PMREGRGD.js";
|
|
20
20
|
import {
|
|
21
21
|
PI_PACKAGE_RESOURCE_FILTERS,
|
|
22
22
|
PiSessionStore,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
registerConfiguredModelProviders,
|
|
32
32
|
setEnvDefault,
|
|
33
33
|
withPiHarnessDefaults
|
|
34
|
-
} from "../chunk-
|
|
34
|
+
} from "../chunk-OABGJVSA.js";
|
|
35
35
|
import {
|
|
36
36
|
safeCapture
|
|
37
37
|
} from "../chunk-AQBXNPMD.js";
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
} from "../chunk-AJZHR626.js";
|
|
41
41
|
import {
|
|
42
42
|
ErrorCode
|
|
43
|
-
} from "../chunk-
|
|
43
|
+
} from "../chunk-M4AY4VBI.js";
|
|
44
44
|
|
|
45
45
|
// src/server/sandbox/direct/createDirectSandbox.ts
|
|
46
46
|
import { spawn } from "child_process";
|
|
@@ -180,7 +180,7 @@ function createDirectSandbox(opts = {}) {
|
|
|
180
180
|
const maxOutputBytes = opts2?.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
181
181
|
const workspaceRoot = runtimeContext.runtimeCwd;
|
|
182
182
|
const cwd = opts2?.cwd ?? workspaceRoot;
|
|
183
|
-
return await new Promise((
|
|
183
|
+
return await new Promise((resolve11, reject) => {
|
|
184
184
|
const child = spawn(cmd, {
|
|
185
185
|
cwd,
|
|
186
186
|
env: withWorkspacePythonEnv({ workspaceRoot, env: opts2?.env, preserveHostHome: true }),
|
|
@@ -209,7 +209,7 @@ function createDirectSandbox(opts = {}) {
|
|
|
209
209
|
if (settled) return;
|
|
210
210
|
settled = true;
|
|
211
211
|
cleanup();
|
|
212
|
-
|
|
212
|
+
resolve11({
|
|
213
213
|
stdout: new Uint8Array(Buffer.concat(stdoutChunks)),
|
|
214
214
|
stderr: new Uint8Array(Buffer.concat(stderrChunks)),
|
|
215
215
|
exitCode: typeof exitCode === "number" ? exitCode : timedOut ? 124 : 1,
|
|
@@ -558,9 +558,9 @@ function createNodeWatcher(root) {
|
|
|
558
558
|
}
|
|
559
559
|
startFsw();
|
|
560
560
|
if (fsw) {
|
|
561
|
-
await new Promise((
|
|
562
|
-
fsw.once("ready",
|
|
563
|
-
fsw.once("error", () =>
|
|
561
|
+
await new Promise((resolve11) => {
|
|
562
|
+
fsw.once("ready", resolve11);
|
|
563
|
+
fsw.once("error", () => resolve11());
|
|
564
564
|
});
|
|
565
565
|
}
|
|
566
566
|
if (closed) return { ok: false, reason: "closed" };
|
|
@@ -969,7 +969,7 @@ function createBwrapSandbox(opts = {}) {
|
|
|
969
969
|
...withSandboxCwd(baseArgs, sandboxCwd),
|
|
970
970
|
...buildCommandArgs(cmd, sandboxOptions.resourceLimits)
|
|
971
971
|
];
|
|
972
|
-
return await new Promise((
|
|
972
|
+
return await new Promise((resolve11, reject) => {
|
|
973
973
|
const child = spawn2("bwrap", args, {
|
|
974
974
|
env: {
|
|
975
975
|
...withWorkspacePythonEnv({ workspaceRoot, env: opts2?.env, sandboxRoot: SANDBOX_HOME2 }),
|
|
@@ -999,7 +999,7 @@ function createBwrapSandbox(opts = {}) {
|
|
|
999
999
|
if (settled) return;
|
|
1000
1000
|
settled = true;
|
|
1001
1001
|
cleanup();
|
|
1002
|
-
|
|
1002
|
+
resolve11({
|
|
1003
1003
|
stdout: new Uint8Array(Buffer.concat(stdoutChunks)),
|
|
1004
1004
|
stderr: new Uint8Array(Buffer.concat(stderrChunks)),
|
|
1005
1005
|
exitCode: typeof exitCode === "number" ? exitCode : timedOut ? 124 : 1,
|
|
@@ -5242,11 +5242,11 @@ async function buildTarGz(files) {
|
|
|
5242
5242
|
}
|
|
5243
5243
|
chunks.push(Buffer.alloc(1024));
|
|
5244
5244
|
const tarBuffer = Buffer.concat(chunks);
|
|
5245
|
-
return new Promise((
|
|
5245
|
+
return new Promise((resolve11, reject) => {
|
|
5246
5246
|
const gzip = createGzip({ level: 6 });
|
|
5247
5247
|
const gzChunks = [];
|
|
5248
5248
|
gzip.on("data", (chunk) => gzChunks.push(chunk));
|
|
5249
|
-
gzip.on("end", () =>
|
|
5249
|
+
gzip.on("end", () => resolve11(Buffer.concat(gzChunks)));
|
|
5250
5250
|
gzip.on("error", reject);
|
|
5251
5251
|
Readable.from(tarBuffer).pipe(gzip);
|
|
5252
5252
|
});
|
|
@@ -6466,13 +6466,13 @@ function resultTool(result) {
|
|
|
6466
6466
|
};
|
|
6467
6467
|
}
|
|
6468
6468
|
function deferred() {
|
|
6469
|
-
let
|
|
6469
|
+
let resolve11;
|
|
6470
6470
|
let reject;
|
|
6471
6471
|
const promise = new Promise((res, rej) => {
|
|
6472
|
-
|
|
6472
|
+
resolve11 = res;
|
|
6473
6473
|
reject = rej;
|
|
6474
6474
|
});
|
|
6475
|
-
return { promise, resolve:
|
|
6475
|
+
return { promise, resolve: resolve11, reject };
|
|
6476
6476
|
}
|
|
6477
6477
|
function errorTool(error) {
|
|
6478
6478
|
const safe = safeError(error);
|
|
@@ -7983,33 +7983,182 @@ function buildHarnessAgentTools(bundle, runtime) {
|
|
|
7983
7983
|
return tools;
|
|
7984
7984
|
}
|
|
7985
7985
|
|
|
7986
|
-
// src/server/http/
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7986
|
+
// src/server/http/sessionChangesTracker.ts
|
|
7987
|
+
var InMemorySessionChangesTracker = class _InMemorySessionChangesTracker {
|
|
7988
|
+
static MAX_CHANGES_PER_SESSION = 1e3;
|
|
7989
|
+
bySession = /* @__PURE__ */ new Map();
|
|
7990
|
+
record(sessionId, change) {
|
|
7991
|
+
const existing = this.bySession.get(sessionId);
|
|
7992
|
+
if (existing) {
|
|
7993
|
+
existing.push(change);
|
|
7994
|
+
if (existing.length > _InMemorySessionChangesTracker.MAX_CHANGES_PER_SESSION) {
|
|
7995
|
+
existing.splice(
|
|
7996
|
+
0,
|
|
7997
|
+
existing.length - _InMemorySessionChangesTracker.MAX_CHANGES_PER_SESSION
|
|
7998
|
+
);
|
|
7999
|
+
}
|
|
8000
|
+
return;
|
|
8001
|
+
}
|
|
8002
|
+
this.bySession.set(sessionId, [change]);
|
|
8003
|
+
}
|
|
8004
|
+
list(sessionId) {
|
|
8005
|
+
return [...this.bySession.get(sessionId) ?? []];
|
|
8006
|
+
}
|
|
8007
|
+
clear(sessionId) {
|
|
8008
|
+
this.bySession.delete(sessionId);
|
|
8009
|
+
}
|
|
8010
|
+
};
|
|
8011
|
+
|
|
8012
|
+
// src/server/runtime/readyStatus.ts
|
|
8013
|
+
function defaultCapabilities(sandboxReady, harnessReady) {
|
|
8014
|
+
return {
|
|
8015
|
+
chat: { state: harnessReady ? "ready" : "preparing" },
|
|
8016
|
+
workspace: { state: sandboxReady ? "ready" : "preparing" },
|
|
8017
|
+
runtimeDependencies: { state: "ready" }
|
|
8018
|
+
};
|
|
8019
|
+
}
|
|
8020
|
+
var ReadyStatusTracker = class {
|
|
8021
|
+
_sandboxReady;
|
|
8022
|
+
_harnessReady;
|
|
8023
|
+
_degradedReason;
|
|
8024
|
+
_capabilities;
|
|
8025
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
8026
|
+
constructor(opts) {
|
|
8027
|
+
this._sandboxReady = opts?.sandboxReady ?? false;
|
|
8028
|
+
this._harnessReady = opts?.harnessReady ?? false;
|
|
8029
|
+
this._capabilities = {
|
|
8030
|
+
...defaultCapabilities(this._sandboxReady, this._harnessReady),
|
|
8031
|
+
...opts?.capabilities ?? {}
|
|
8032
|
+
};
|
|
8033
|
+
}
|
|
8034
|
+
get state() {
|
|
8035
|
+
if (this._degradedReason) return "degraded";
|
|
8036
|
+
return this._sandboxReady && this._harnessReady ? "ready" : "provisioning";
|
|
8037
|
+
}
|
|
8038
|
+
isReady() {
|
|
8039
|
+
return this.state === "ready";
|
|
8040
|
+
}
|
|
8041
|
+
getReadiness() {
|
|
7990
8042
|
return {
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
8043
|
+
sandboxReady: this._sandboxReady,
|
|
8044
|
+
harnessReady: this._harnessReady,
|
|
8045
|
+
capabilities: this.getCapabilities(),
|
|
8046
|
+
degradedReason: this._degradedReason
|
|
7994
8047
|
};
|
|
7995
|
-
}
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8048
|
+
}
|
|
8049
|
+
getCapabilities() {
|
|
8050
|
+
return {
|
|
8051
|
+
chat: { ...this._capabilities.chat },
|
|
8052
|
+
workspace: { ...this._capabilities.workspace },
|
|
8053
|
+
runtimeDependencies: { ...this._capabilities.runtimeDependencies }
|
|
8054
|
+
};
|
|
8055
|
+
}
|
|
8056
|
+
updateCapability(name, detail) {
|
|
8057
|
+
this._capabilities = {
|
|
8058
|
+
...this._capabilities,
|
|
8059
|
+
[name]: { ...detail }
|
|
8060
|
+
};
|
|
8061
|
+
this.emit();
|
|
8062
|
+
}
|
|
8063
|
+
updateRuntimeDependencies(detail) {
|
|
8064
|
+
this.updateCapability("runtimeDependencies", detail);
|
|
8065
|
+
}
|
|
8066
|
+
markSandboxReady() {
|
|
8067
|
+
if (this._sandboxReady) return;
|
|
8068
|
+
this._sandboxReady = true;
|
|
8069
|
+
if (this._capabilities.workspace.state === "preparing") {
|
|
8070
|
+
this._capabilities.workspace = { state: "ready" };
|
|
8003
8071
|
}
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8072
|
+
this.emit();
|
|
8073
|
+
}
|
|
8074
|
+
markHarnessReady() {
|
|
8075
|
+
if (this._harnessReady) return;
|
|
8076
|
+
this._harnessReady = true;
|
|
8077
|
+
if (this._capabilities.chat.state === "preparing") {
|
|
8078
|
+
this._capabilities.chat = { state: "ready" };
|
|
8079
|
+
}
|
|
8080
|
+
this.emit();
|
|
8081
|
+
}
|
|
8082
|
+
markDegraded(reason) {
|
|
8083
|
+
this._degradedReason = reason;
|
|
8084
|
+
this.emit();
|
|
8085
|
+
}
|
|
8086
|
+
clearDegraded() {
|
|
8087
|
+
if (!this._degradedReason) return;
|
|
8088
|
+
this._degradedReason = void 0;
|
|
8089
|
+
this.emit();
|
|
8090
|
+
}
|
|
8091
|
+
subscribe(handler) {
|
|
8092
|
+
this.subscribers.add(handler);
|
|
8093
|
+
handler(this.snapshot());
|
|
8094
|
+
return () => {
|
|
8095
|
+
this.subscribers.delete(handler);
|
|
8096
|
+
};
|
|
8097
|
+
}
|
|
8098
|
+
emit() {
|
|
8099
|
+
const event = this.snapshot();
|
|
8100
|
+
for (const h of this.subscribers) h(event);
|
|
8101
|
+
}
|
|
8102
|
+
snapshot() {
|
|
8103
|
+
return {
|
|
8104
|
+
state: this.state,
|
|
8105
|
+
sandboxReady: this._sandboxReady,
|
|
8106
|
+
harnessReady: this._harnessReady,
|
|
8107
|
+
capabilities: this.getCapabilities(),
|
|
8108
|
+
message: this._degradedReason,
|
|
8109
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8110
|
+
};
|
|
8111
|
+
}
|
|
8112
|
+
};
|
|
8113
|
+
|
|
8114
|
+
// src/server/runtime/modeReadiness.ts
|
|
8115
|
+
function createRuntimeReadyStatusTracker(modeAdapter, opts) {
|
|
8116
|
+
const readiness = modeAdapter.readiness;
|
|
8117
|
+
const tracker = new ReadyStatusTracker({
|
|
8118
|
+
sandboxReady: readiness?.initialSandboxReady ?? true,
|
|
8119
|
+
harnessReady: opts.harnessReady,
|
|
8120
|
+
capabilities: {
|
|
8121
|
+
...readiness?.initialWorkspaceReadiness ? { workspace: readiness.initialWorkspaceReadiness } : {},
|
|
8122
|
+
...opts.capabilities ?? {}
|
|
8009
8123
|
}
|
|
8010
|
-
return { status: "ready" };
|
|
8011
8124
|
});
|
|
8012
|
-
|
|
8125
|
+
readiness?.onTrackerCreated?.(tracker);
|
|
8126
|
+
return tracker;
|
|
8127
|
+
}
|
|
8128
|
+
|
|
8129
|
+
// src/server/tools/pluginDiagnostics.ts
|
|
8130
|
+
function createPluginDiagnosticsTool(deps) {
|
|
8131
|
+
return {
|
|
8132
|
+
name: "plugin_diagnostics",
|
|
8133
|
+
description: [
|
|
8134
|
+
"Return current plugin/skill loading errors plus the diagnostics from the",
|
|
8135
|
+
"last /reload. Call this after asking the user to run /reload \u2014 or whenever a",
|
|
8136
|
+
"plugin or skill you expected does not appear to be loaded \u2014 then fix the",
|
|
8137
|
+
"reported error and ask the user to /reload again. Returns a JSON object with",
|
|
8138
|
+
"lastReloadDiagnostics, resourceDiagnostics, and pluginErrors arrays; an empty",
|
|
8139
|
+
"result means no load errors were detected."
|
|
8140
|
+
].join(" "),
|
|
8141
|
+
parameters: {
|
|
8142
|
+
type: "object",
|
|
8143
|
+
properties: {},
|
|
8144
|
+
additionalProperties: false
|
|
8145
|
+
},
|
|
8146
|
+
async execute(_params, ctx) {
|
|
8147
|
+
const harness = deps.getHarness();
|
|
8148
|
+
const resourceDiagnostics = harness?.getResourceDiagnostics?.(ctx.sessionId ?? "") ?? [];
|
|
8149
|
+
const pluginErrors = deps.getPluginErrors ? await deps.getPluginErrors() : [];
|
|
8150
|
+
const payload = {
|
|
8151
|
+
lastReloadDiagnostics: deps.getLastReloadDiagnostics(),
|
|
8152
|
+
resourceDiagnostics,
|
|
8153
|
+
pluginErrors
|
|
8154
|
+
};
|
|
8155
|
+
return {
|
|
8156
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
8157
|
+
details: payload,
|
|
8158
|
+
isError: false
|
|
8159
|
+
};
|
|
8160
|
+
}
|
|
8161
|
+
};
|
|
8013
8162
|
}
|
|
8014
8163
|
|
|
8015
8164
|
// src/server/http/fsEventBroadcaster.ts
|
|
@@ -8404,12 +8553,20 @@ function modelsRoutes(app, opts, done) {
|
|
|
8404
8553
|
}
|
|
8405
8554
|
|
|
8406
8555
|
// src/server/http/routes/skills.ts
|
|
8556
|
+
import { isAbsolute as isAbsolute9, relative as relative13, resolve as resolve10, sep as sep7 } from "path";
|
|
8407
8557
|
import {
|
|
8408
8558
|
DefaultPackageManager,
|
|
8409
8559
|
getAgentDir,
|
|
8410
8560
|
loadSkills
|
|
8411
8561
|
} from "@mariozechner/pi-coding-agent";
|
|
8412
8562
|
var CACHE_TTL_MS2 = 3e4;
|
|
8563
|
+
function pathForWorkspaceEditor(workspaceRoot, filePath) {
|
|
8564
|
+
const pathWithinWorkspace = relative13(resolve10(workspaceRoot), resolve10(filePath));
|
|
8565
|
+
if (pathWithinWorkspace === "" || pathWithinWorkspace === ".." || pathWithinWorkspace.startsWith(`..${sep7}`) || isAbsolute9(pathWithinWorkspace)) {
|
|
8566
|
+
return filePath;
|
|
8567
|
+
}
|
|
8568
|
+
return pathWithinWorkspace.split(sep7).join("/");
|
|
8569
|
+
}
|
|
8413
8570
|
function skillsRoutes(app, opts, done) {
|
|
8414
8571
|
const cached = /* @__PURE__ */ new Map();
|
|
8415
8572
|
async function resolveSkillsForRequest(request, refresh = false) {
|
|
@@ -8448,7 +8605,7 @@ function skillsRoutes(app, opts, done) {
|
|
|
8448
8605
|
const skills = result.skills.map((s) => ({
|
|
8449
8606
|
name: String(s.name),
|
|
8450
8607
|
description: String(s.description ?? ""),
|
|
8451
|
-
...typeof s.filePath === "string" ? { filePath: s.filePath } : {},
|
|
8608
|
+
...typeof s.filePath === "string" ? { filePath: pathForWorkspaceEditor(workspaceRoot, s.filePath) } : {},
|
|
8452
8609
|
...typeof s.sourceInfo?.scope === "string" ? { source: s.sourceInfo.scope } : {}
|
|
8453
8610
|
}));
|
|
8454
8611
|
const entry = { skills, expiresAt: now + CACHE_TTL_MS2 };
|
|
@@ -8510,7 +8667,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8510
8667
|
try {
|
|
8511
8668
|
const service = await resolveService(opts, request);
|
|
8512
8669
|
if (!service.listSessions) throw unsupportedServiceMethod("list Pi chat sessions");
|
|
8513
|
-
return reply.send(await service.listSessions(getRequestContext(request), sessionListOptions(request)));
|
|
8670
|
+
return reply.send(await service.listSessions(getRequestContext(request, opts), sessionListOptions(request)));
|
|
8514
8671
|
} catch (err) {
|
|
8515
8672
|
return sendRouteError(reply, err, "list pi chat sessions failed");
|
|
8516
8673
|
}
|
|
@@ -8521,7 +8678,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8521
8678
|
try {
|
|
8522
8679
|
const service = await resolveService(opts, request);
|
|
8523
8680
|
if (!service.createSession) throw unsupportedServiceMethod("create Pi chat session");
|
|
8524
|
-
return reply.code(201).send(await service.createSession(getRequestContext(request), body));
|
|
8681
|
+
return reply.code(201).send(await service.createSession(getRequestContext(request, opts), body));
|
|
8525
8682
|
} catch (err) {
|
|
8526
8683
|
return sendRouteError(reply, err, "create pi chat session failed");
|
|
8527
8684
|
}
|
|
@@ -8532,7 +8689,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8532
8689
|
try {
|
|
8533
8690
|
const service = await resolveService(opts, request);
|
|
8534
8691
|
if (!service.deleteSession) throw unsupportedServiceMethod("delete Pi chat session");
|
|
8535
|
-
await service.deleteSession(getRequestContext(request), params.sessionId);
|
|
8692
|
+
await service.deleteSession(getRequestContext(request, opts), params.sessionId);
|
|
8536
8693
|
return reply.code(204).send();
|
|
8537
8694
|
} catch (err) {
|
|
8538
8695
|
return sendRouteError(reply, err, "delete pi chat session failed");
|
|
@@ -8543,7 +8700,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8543
8700
|
if (!params) return;
|
|
8544
8701
|
try {
|
|
8545
8702
|
const service = await resolveService(opts, request);
|
|
8546
|
-
const snapshot = await service.readState(getRequestContext(request), params.sessionId);
|
|
8703
|
+
const snapshot = await service.readState(getRequestContext(request, opts), params.sessionId);
|
|
8547
8704
|
return reply.send(PiChatSnapshotSchema.parse(snapshot));
|
|
8548
8705
|
} catch (err) {
|
|
8549
8706
|
return sendRouteError(reply, err, "read pi chat state failed");
|
|
@@ -8564,7 +8721,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8564
8721
|
};
|
|
8565
8722
|
try {
|
|
8566
8723
|
const service = await resolveService(opts, request);
|
|
8567
|
-
const result = await service.subscribe(getRequestContext(request), params.sessionId, query.cursor, writeFrame);
|
|
8724
|
+
const result = await service.subscribe(getRequestContext(request, opts), params.sessionId, query.cursor, writeFrame);
|
|
8568
8725
|
if (result.type !== "ok") {
|
|
8569
8726
|
return sendReplayRangeError(reply, result);
|
|
8570
8727
|
}
|
|
@@ -8598,7 +8755,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8598
8755
|
if (!body) return;
|
|
8599
8756
|
try {
|
|
8600
8757
|
const service = await resolveService(opts, request);
|
|
8601
|
-
const receipt = await service.prompt(getRequestContext(request), params.sessionId, body);
|
|
8758
|
+
const receipt = await service.prompt(getRequestContext(request, opts), params.sessionId, body);
|
|
8602
8759
|
return reply.code(202).send(receipt);
|
|
8603
8760
|
} catch (err) {
|
|
8604
8761
|
return sendRouteError(reply, err, "prompt rejected");
|
|
@@ -8611,7 +8768,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8611
8768
|
if (!body) return;
|
|
8612
8769
|
try {
|
|
8613
8770
|
const service = await resolveService(opts, request);
|
|
8614
|
-
const receipt = await service.followUp(getRequestContext(request), params.sessionId, body);
|
|
8771
|
+
const receipt = await service.followUp(getRequestContext(request, opts), params.sessionId, body);
|
|
8615
8772
|
return reply.code(202).send(receipt);
|
|
8616
8773
|
} catch (err) {
|
|
8617
8774
|
return sendRouteError(reply, err, "follow-up rejected");
|
|
@@ -8624,7 +8781,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8624
8781
|
if (!body) return;
|
|
8625
8782
|
try {
|
|
8626
8783
|
const service = await resolveService(opts, request);
|
|
8627
|
-
const receipt = await service.clearQueue(getRequestContext(request), params.sessionId, body);
|
|
8784
|
+
const receipt = await service.clearQueue(getRequestContext(request, opts), params.sessionId, body);
|
|
8628
8785
|
return reply.code(202).send(receipt);
|
|
8629
8786
|
} catch (err) {
|
|
8630
8787
|
return sendRouteError(reply, err, "queue clear rejected");
|
|
@@ -8637,7 +8794,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8637
8794
|
if (!body) return;
|
|
8638
8795
|
try {
|
|
8639
8796
|
const service = await resolveService(opts, request);
|
|
8640
|
-
const receipt = await service.interrupt(getRequestContext(request), params.sessionId, body);
|
|
8797
|
+
const receipt = await service.interrupt(getRequestContext(request, opts), params.sessionId, body);
|
|
8641
8798
|
return reply.code(202).send(receipt);
|
|
8642
8799
|
} catch (err) {
|
|
8643
8800
|
return sendRouteError(reply, err, "interrupt rejected");
|
|
@@ -8650,7 +8807,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8650
8807
|
if (!body) return;
|
|
8651
8808
|
try {
|
|
8652
8809
|
const service = await resolveService(opts, request);
|
|
8653
|
-
const receipt = await service.stop(getRequestContext(request), params.sessionId, body);
|
|
8810
|
+
const receipt = await service.stop(getRequestContext(request, opts), params.sessionId, body);
|
|
8654
8811
|
return reply.code(202).send(receipt);
|
|
8655
8812
|
} catch (err) {
|
|
8656
8813
|
return sendRouteError(reply, err, "stop rejected");
|
|
@@ -8710,13 +8867,14 @@ async function resolveService(opts, request) {
|
|
|
8710
8867
|
}
|
|
8711
8868
|
return service;
|
|
8712
8869
|
}
|
|
8713
|
-
function getRequestContext(request) {
|
|
8870
|
+
function getRequestContext(request, opts) {
|
|
8714
8871
|
const storageScopeHeader = request.headers["x-boring-storage-scope"];
|
|
8715
8872
|
const user = request.user;
|
|
8716
8873
|
const authSubject = user?.id;
|
|
8717
8874
|
const authEmail = user?.email;
|
|
8875
|
+
const workspaceId = opts.defaultWorkspaceId === false ? void 0 : request.workspaceContext?.workspaceId ?? opts.defaultWorkspaceId ?? DEFAULT_WORKSPACE_ID2;
|
|
8718
8876
|
return {
|
|
8719
|
-
workspaceId
|
|
8877
|
+
workspaceId,
|
|
8720
8878
|
storageScope: typeof storageScopeHeader === "string" && storageScopeHeader.length > 0 ? storageScopeHeader : void 0,
|
|
8721
8879
|
authSubject: typeof authSubject === "string" && authSubject.length > 0 ? authSubject : void 0,
|
|
8722
8880
|
authEmail: typeof authEmail === "string" && authEmail.length > 0 ? authEmail : void 0,
|
|
@@ -8809,32 +8967,6 @@ function systemPromptRoutes(app, opts, done) {
|
|
|
8809
8967
|
done();
|
|
8810
8968
|
}
|
|
8811
8969
|
|
|
8812
|
-
// src/server/http/sessionChangesTracker.ts
|
|
8813
|
-
var InMemorySessionChangesTracker = class _InMemorySessionChangesTracker {
|
|
8814
|
-
static MAX_CHANGES_PER_SESSION = 1e3;
|
|
8815
|
-
bySession = /* @__PURE__ */ new Map();
|
|
8816
|
-
record(sessionId, change) {
|
|
8817
|
-
const existing = this.bySession.get(sessionId);
|
|
8818
|
-
if (existing) {
|
|
8819
|
-
existing.push(change);
|
|
8820
|
-
if (existing.length > _InMemorySessionChangesTracker.MAX_CHANGES_PER_SESSION) {
|
|
8821
|
-
existing.splice(
|
|
8822
|
-
0,
|
|
8823
|
-
existing.length - _InMemorySessionChangesTracker.MAX_CHANGES_PER_SESSION
|
|
8824
|
-
);
|
|
8825
|
-
}
|
|
8826
|
-
return;
|
|
8827
|
-
}
|
|
8828
|
-
this.bySession.set(sessionId, [change]);
|
|
8829
|
-
}
|
|
8830
|
-
list(sessionId) {
|
|
8831
|
-
return [...this.bySession.get(sessionId) ?? []];
|
|
8832
|
-
}
|
|
8833
|
-
clear(sessionId) {
|
|
8834
|
-
this.bySession.delete(sessionId);
|
|
8835
|
-
}
|
|
8836
|
-
};
|
|
8837
|
-
|
|
8838
8970
|
// src/server/http/routes/sessionChanges.ts
|
|
8839
8971
|
function requireSessionId(value, reply) {
|
|
8840
8972
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -9163,7 +9295,7 @@ function searchRoutes(app, opts, done) {
|
|
|
9163
9295
|
}
|
|
9164
9296
|
|
|
9165
9297
|
// src/server/git/gitFileUrl.ts
|
|
9166
|
-
import { dirname as dirname12, relative as
|
|
9298
|
+
import { dirname as dirname12, relative as relative14 } from "path";
|
|
9167
9299
|
import { execFile as execFile3 } from "child_process";
|
|
9168
9300
|
import { promisify as promisify3 } from "util";
|
|
9169
9301
|
|
|
@@ -9246,7 +9378,7 @@ async function resolveGitFileUrl(workspaceRoot, path4) {
|
|
|
9246
9378
|
commitSha = null;
|
|
9247
9379
|
}
|
|
9248
9380
|
}
|
|
9249
|
-
const repoRelativePath =
|
|
9381
|
+
const repoRelativePath = relative14(repoRoot, absolutePath).replace(/\\/g, "/");
|
|
9250
9382
|
const url = buildGitFileUrl({ remoteUrl, repoRelativePath, branch, commitSha });
|
|
9251
9383
|
if (!url) {
|
|
9252
9384
|
return disabled("Only GitHub SSH/HTTPS remotes are supported right now.");
|
|
@@ -9298,167 +9430,136 @@ function gitRoutes(app, opts, done) {
|
|
|
9298
9430
|
done();
|
|
9299
9431
|
}
|
|
9300
9432
|
|
|
9301
|
-
// src/server/
|
|
9302
|
-
function
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
workspace: { state: sandboxReady ? "ready" : "preparing" },
|
|
9306
|
-
runtimeDependencies: { state: "ready" }
|
|
9307
|
-
};
|
|
9308
|
-
}
|
|
9309
|
-
var ReadyStatusTracker = class {
|
|
9310
|
-
_sandboxReady;
|
|
9311
|
-
_harnessReady;
|
|
9312
|
-
_degradedReason;
|
|
9313
|
-
_capabilities;
|
|
9314
|
-
subscribers = /* @__PURE__ */ new Set();
|
|
9315
|
-
constructor(opts) {
|
|
9316
|
-
this._sandboxReady = opts?.sandboxReady ?? false;
|
|
9317
|
-
this._harnessReady = opts?.harnessReady ?? false;
|
|
9318
|
-
this._capabilities = {
|
|
9319
|
-
...defaultCapabilities(this._sandboxReady, this._harnessReady),
|
|
9320
|
-
...opts?.capabilities ?? {}
|
|
9321
|
-
};
|
|
9322
|
-
}
|
|
9323
|
-
get state() {
|
|
9324
|
-
if (this._degradedReason) return "degraded";
|
|
9325
|
-
return this._sandboxReady && this._harnessReady ? "ready" : "provisioning";
|
|
9326
|
-
}
|
|
9327
|
-
isReady() {
|
|
9328
|
-
return this.state === "ready";
|
|
9329
|
-
}
|
|
9330
|
-
getReadiness() {
|
|
9433
|
+
// src/server/http/routes/health.ts
|
|
9434
|
+
function healthRoutes(app, opts, done) {
|
|
9435
|
+
const startTime = Date.now();
|
|
9436
|
+
app.get("/health", async () => {
|
|
9331
9437
|
return {
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
degradedReason: this._degradedReason
|
|
9336
|
-
};
|
|
9337
|
-
}
|
|
9338
|
-
getCapabilities() {
|
|
9339
|
-
return {
|
|
9340
|
-
chat: { ...this._capabilities.chat },
|
|
9341
|
-
workspace: { ...this._capabilities.workspace },
|
|
9342
|
-
runtimeDependencies: { ...this._capabilities.runtimeDependencies }
|
|
9343
|
-
};
|
|
9344
|
-
}
|
|
9345
|
-
updateCapability(name, detail) {
|
|
9346
|
-
this._capabilities = {
|
|
9347
|
-
...this._capabilities,
|
|
9348
|
-
[name]: { ...detail }
|
|
9438
|
+
status: "ok",
|
|
9439
|
+
version: opts.version,
|
|
9440
|
+
uptime: Math.floor((Date.now() - startTime) / 1e3)
|
|
9349
9441
|
};
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
if (this._capabilities.workspace.state === "preparing") {
|
|
9359
|
-
this._capabilities.workspace = { state: "ready" };
|
|
9360
|
-
}
|
|
9361
|
-
this.emit();
|
|
9362
|
-
}
|
|
9363
|
-
markHarnessReady() {
|
|
9364
|
-
if (this._harnessReady) return;
|
|
9365
|
-
this._harnessReady = true;
|
|
9366
|
-
if (this._capabilities.chat.state === "preparing") {
|
|
9367
|
-
this._capabilities.chat = { state: "ready" };
|
|
9442
|
+
});
|
|
9443
|
+
app.get("/ready", async (_request, reply) => {
|
|
9444
|
+
const state = opts.getReadiness();
|
|
9445
|
+
if (state.degradedReason) {
|
|
9446
|
+
return reply.code(503).send({
|
|
9447
|
+
status: "degraded",
|
|
9448
|
+
reason: state.degradedReason
|
|
9449
|
+
});
|
|
9368
9450
|
}
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
}
|
|
9375
|
-
clearDegraded() {
|
|
9376
|
-
if (!this._degradedReason) return;
|
|
9377
|
-
this._degradedReason = void 0;
|
|
9378
|
-
this.emit();
|
|
9379
|
-
}
|
|
9380
|
-
subscribe(handler) {
|
|
9381
|
-
this.subscribers.add(handler);
|
|
9382
|
-
handler(this.snapshot());
|
|
9383
|
-
return () => {
|
|
9384
|
-
this.subscribers.delete(handler);
|
|
9385
|
-
};
|
|
9386
|
-
}
|
|
9387
|
-
emit() {
|
|
9388
|
-
const event = this.snapshot();
|
|
9389
|
-
for (const h of this.subscribers) h(event);
|
|
9390
|
-
}
|
|
9391
|
-
snapshot() {
|
|
9392
|
-
return {
|
|
9393
|
-
state: this.state,
|
|
9394
|
-
sandboxReady: this._sandboxReady,
|
|
9395
|
-
harnessReady: this._harnessReady,
|
|
9396
|
-
capabilities: this.getCapabilities(),
|
|
9397
|
-
message: this._degradedReason,
|
|
9398
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9399
|
-
};
|
|
9400
|
-
}
|
|
9401
|
-
};
|
|
9402
|
-
|
|
9403
|
-
// src/server/runtime/modeReadiness.ts
|
|
9404
|
-
function createRuntimeReadyStatusTracker(modeAdapter, opts) {
|
|
9405
|
-
const readiness = modeAdapter.readiness;
|
|
9406
|
-
const tracker = new ReadyStatusTracker({
|
|
9407
|
-
sandboxReady: readiness?.initialSandboxReady ?? true,
|
|
9408
|
-
harnessReady: opts.harnessReady,
|
|
9409
|
-
capabilities: {
|
|
9410
|
-
...readiness?.initialWorkspaceReadiness ? { workspace: readiness.initialWorkspaceReadiness } : {},
|
|
9411
|
-
...opts.capabilities ?? {}
|
|
9451
|
+
if (!state.sandboxReady || !state.harnessReady) {
|
|
9452
|
+
return reply.code(503).send({
|
|
9453
|
+
status: "provisioning",
|
|
9454
|
+
retryAfter: 2
|
|
9455
|
+
});
|
|
9412
9456
|
}
|
|
9457
|
+
return { status: "ready" };
|
|
9413
9458
|
});
|
|
9414
|
-
|
|
9415
|
-
return tracker;
|
|
9459
|
+
done();
|
|
9416
9460
|
}
|
|
9417
9461
|
|
|
9418
|
-
// src/server/
|
|
9419
|
-
function
|
|
9420
|
-
return
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
};
|
|
9462
|
+
// src/server/agentRouteBindingProfile.ts
|
|
9463
|
+
function toolNames(tools) {
|
|
9464
|
+
return tools.map((tool) => tool.name);
|
|
9465
|
+
}
|
|
9466
|
+
async function registerAgentRouteBindingProfile(app, profile) {
|
|
9467
|
+
if (profile.dispose) {
|
|
9468
|
+
app.addHook("onClose", async () => {
|
|
9469
|
+
await profile.dispose?.();
|
|
9470
|
+
});
|
|
9471
|
+
}
|
|
9472
|
+
await profile.beforeRegister?.(app);
|
|
9473
|
+
const { register = true, ...health } = profile.health;
|
|
9474
|
+
if (register) await app.register(healthRoutes, health);
|
|
9475
|
+
if (profile.filesystem) {
|
|
9476
|
+
await app.register(fileRoutes, profile.filesystem.file);
|
|
9477
|
+
await app.register(fsEventsRoutes, profile.filesystem.fsEvents);
|
|
9478
|
+
await app.register(treeRoutes, profile.filesystem.tree);
|
|
9479
|
+
await app.register(searchRoutes, profile.filesystem.search);
|
|
9480
|
+
await app.register(gitRoutes, profile.filesystem.git);
|
|
9481
|
+
}
|
|
9482
|
+
await app.register(piChatRoutes, profile.chat);
|
|
9483
|
+
if (profile.systemPrompt) await app.register(systemPromptRoutes, profile.systemPrompt);
|
|
9484
|
+
await app.register(modelsRoutes, profile.models ?? {});
|
|
9485
|
+
if (profile.skills) await app.register(skillsRoutes, profile.skills);
|
|
9486
|
+
await app.register(sessionChangesRoutes, { tracker: profile.sessionChangesTracker });
|
|
9487
|
+
if (profile.reload) {
|
|
9488
|
+
if (typeof profile.reload === "function") {
|
|
9489
|
+
await profile.reload(app);
|
|
9490
|
+
} else {
|
|
9491
|
+
await app.register(reloadRoutes, profile.reload);
|
|
9449
9492
|
}
|
|
9450
|
-
}
|
|
9493
|
+
}
|
|
9494
|
+
await app.register(catalogRoutes, profile.catalog);
|
|
9495
|
+
if (profile.commands) await app.register(commandsRoutes, profile.commands);
|
|
9496
|
+
await app.register(readyStatusRoutes, profile.readyStatus);
|
|
9451
9497
|
}
|
|
9452
9498
|
|
|
9453
9499
|
// src/server/createAgentApp.ts
|
|
9454
9500
|
var DEFAULT_VERSION = "0.1.0-dev";
|
|
9455
9501
|
var DEFAULT_SESSION_ID = "default";
|
|
9502
|
+
var PURE_RUNTIME_MODE = "none";
|
|
9456
9503
|
async function createAgentApp(opts = {}) {
|
|
9457
|
-
const workspaceRoot = opts.workspaceRoot ?? process.cwd();
|
|
9458
9504
|
const sessionId = opts.sessionId ?? DEFAULT_SESSION_ID;
|
|
9459
|
-
const templatePath = opts.templatePath ?? getEnv("BORING_AGENT_TEMPLATE_PATH");
|
|
9460
9505
|
const app = Fastify({ logger: opts.logger ?? true, bodyLimit: 16 * 1024 * 1024 });
|
|
9461
9506
|
const resolvedMode = opts.runtimeModeAdapter?.id ?? opts.mode ?? autoDetectMode();
|
|
9507
|
+
const profile = !opts.runtimeModeAdapter && resolvedMode === PURE_RUNTIME_MODE ? await createPureAgentAppProfile(opts, resolvedMode) : await createWorkspaceAgentAppProfile(opts, sessionId, resolvedMode, app);
|
|
9508
|
+
app.addHook(
|
|
9509
|
+
"onRequest",
|
|
9510
|
+
createAuthMiddleware({
|
|
9511
|
+
authToken: opts.authToken,
|
|
9512
|
+
publicPaths: ["/health", "/ready", "/api/v1/ready-status"]
|
|
9513
|
+
})
|
|
9514
|
+
);
|
|
9515
|
+
await registerAgentRouteBindingProfile(app, profile);
|
|
9516
|
+
return app;
|
|
9517
|
+
}
|
|
9518
|
+
async function createPureAgentAppProfile(opts, resolvedMode) {
|
|
9519
|
+
const runtimePi = withPiHarnessDefaults(opts.pi);
|
|
9520
|
+
const baseHarnessFactory = opts.harnessFactory ?? ((input) => createPiCodingAgentHarness({
|
|
9521
|
+
...input,
|
|
9522
|
+
pi: runtimePi
|
|
9523
|
+
}));
|
|
9524
|
+
const harnessFactory = ((input) => baseHarnessFactory({
|
|
9525
|
+
...input,
|
|
9526
|
+
sessionNamespace: opts.sessionNamespace,
|
|
9527
|
+
sessionRoot: opts.sessionRoot,
|
|
9528
|
+
sessionDir: opts.sessionDir ?? input.sessionDir
|
|
9529
|
+
}));
|
|
9530
|
+
const tools = opts.extraTools ?? [];
|
|
9531
|
+
const coreAgent = createAgentRuntimeBridge({
|
|
9532
|
+
runtime: "none",
|
|
9533
|
+
tools,
|
|
9534
|
+
harnessFactory,
|
|
9535
|
+
systemPromptAppend: opts.systemPromptAppend,
|
|
9536
|
+
systemPromptDynamic: opts.systemPromptDynamic,
|
|
9537
|
+
telemetry: opts.telemetry,
|
|
9538
|
+
metering: opts.metering,
|
|
9539
|
+
sessionStorageRoot: opts.sessionRoot
|
|
9540
|
+
});
|
|
9541
|
+
const agentRuntime = await coreAgent.getRuntime();
|
|
9542
|
+
const readyTracker = new ReadyStatusTracker({ sandboxReady: true, harnessReady: true });
|
|
9543
|
+
return {
|
|
9544
|
+
runtimeMode: resolvedMode,
|
|
9545
|
+
capabilities: { tools: toolNames(tools) },
|
|
9546
|
+
sessionChangesTracker: new InMemorySessionChangesTracker(),
|
|
9547
|
+
health: {
|
|
9548
|
+
version: opts.version ?? DEFAULT_VERSION,
|
|
9549
|
+
getReadiness: () => ({ sandboxReady: true, harnessReady: true })
|
|
9550
|
+
},
|
|
9551
|
+
chat: {
|
|
9552
|
+
service: agentRuntime.service,
|
|
9553
|
+
defaultWorkspaceId: false
|
|
9554
|
+
},
|
|
9555
|
+
catalog: { tools },
|
|
9556
|
+
readyStatus: { tracker: readyTracker },
|
|
9557
|
+
dispose: () => coreAgent.agent.dispose()
|
|
9558
|
+
};
|
|
9559
|
+
}
|
|
9560
|
+
async function createWorkspaceAgentAppProfile(opts, sessionId, resolvedMode, app) {
|
|
9561
|
+
const workspaceRoot = opts.workspaceRoot ?? process.cwd();
|
|
9562
|
+
const templatePath = opts.templatePath ?? getEnv("BORING_AGENT_TEMPLATE_PATH");
|
|
9462
9563
|
const modeAdapter = opts.runtimeModeAdapter ?? resolveMode(resolvedMode);
|
|
9463
9564
|
let runtimeBundle = await modeAdapter.create({
|
|
9464
9565
|
workspaceRoot,
|
|
@@ -9536,7 +9637,8 @@ async function createAgentApp(opts = {}) {
|
|
|
9536
9637
|
const harnessFactory = ((input) => baseHarnessFactory({
|
|
9537
9638
|
...input,
|
|
9538
9639
|
sessionNamespace: opts.sessionNamespace,
|
|
9539
|
-
|
|
9640
|
+
sessionRoot: opts.sessionRoot,
|
|
9641
|
+
sessionDir: opts.sessionDir ?? input.sessionDir
|
|
9540
9642
|
}));
|
|
9541
9643
|
const coreAgent = createAgentRuntimeBridge({
|
|
9542
9644
|
runtime: modeAdapter,
|
|
@@ -9546,6 +9648,7 @@ async function createAgentApp(opts = {}) {
|
|
|
9546
9648
|
systemPromptDynamic: opts.systemPromptDynamic,
|
|
9547
9649
|
telemetry: opts.telemetry,
|
|
9548
9650
|
metering: opts.metering,
|
|
9651
|
+
sessionStorageRoot: opts.sessionRoot,
|
|
9549
9652
|
workdir: workspaceRoot
|
|
9550
9653
|
}, {
|
|
9551
9654
|
service: {
|
|
@@ -9556,21 +9659,9 @@ async function createAgentApp(opts = {}) {
|
|
|
9556
9659
|
const agentRuntime = await coreAgent.getRuntime();
|
|
9557
9660
|
const harness = agentRuntime.harness;
|
|
9558
9661
|
harnessRef = harness;
|
|
9559
|
-
const sessionChangesTracker = new InMemorySessionChangesTracker();
|
|
9560
9662
|
const readyTracker = createRuntimeReadyStatusTracker(modeAdapter, {
|
|
9561
9663
|
harnessReady: true
|
|
9562
9664
|
});
|
|
9563
|
-
app.addHook(
|
|
9564
|
-
"onRequest",
|
|
9565
|
-
createAuthMiddleware({
|
|
9566
|
-
authToken: opts.authToken,
|
|
9567
|
-
publicPaths: ["/health", "/ready", "/api/v1/ready-status"]
|
|
9568
|
-
})
|
|
9569
|
-
);
|
|
9570
|
-
await app.register(healthRoutes, {
|
|
9571
|
-
version: opts.version ?? DEFAULT_VERSION,
|
|
9572
|
-
getReadiness: () => readyTracker.getReadiness()
|
|
9573
|
-
});
|
|
9574
9665
|
const filesystemBindingsForRequest = opts.getFilesystemBindings ? (request) => {
|
|
9575
9666
|
const user = request.user;
|
|
9576
9667
|
return opts.getFilesystemBindings?.({
|
|
@@ -9583,46 +9674,65 @@ async function createAgentApp(opts = {}) {
|
|
|
9583
9674
|
requestId: request.id
|
|
9584
9675
|
});
|
|
9585
9676
|
} : void 0;
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9677
|
+
return {
|
|
9678
|
+
runtimeMode: resolvedMode,
|
|
9679
|
+
capabilities: { tools: toolNames(tools) },
|
|
9680
|
+
sessionChangesTracker: new InMemorySessionChangesTracker(),
|
|
9681
|
+
health: {
|
|
9682
|
+
version: opts.version ?? DEFAULT_VERSION,
|
|
9683
|
+
getReadiness: () => readyTracker.getReadiness()
|
|
9684
|
+
},
|
|
9685
|
+
filesystem: {
|
|
9686
|
+
file: {
|
|
9687
|
+
workspace: runtimeBundle.workspace,
|
|
9688
|
+
getFilesystemBindings: filesystemBindingsForRequest,
|
|
9689
|
+
filesystemBindings: runtimeBundle.filesystemBindings
|
|
9690
|
+
},
|
|
9691
|
+
fsEvents: { workspace: runtimeBundle.workspace },
|
|
9692
|
+
tree: {
|
|
9693
|
+
workspace: runtimeBundle.workspace,
|
|
9694
|
+
getFilesystemBindings: filesystemBindingsForRequest,
|
|
9695
|
+
filesystemBindings: runtimeBundle.filesystemBindings
|
|
9696
|
+
},
|
|
9697
|
+
// File search shares the same bound implementation as the model tool.
|
|
9698
|
+
search: { fileSearch: runtimeBundle.fileSearch },
|
|
9699
|
+
// Git metadata resolves against host storage, not a sandbox-internal cwd.
|
|
9700
|
+
git: { getWorkspaceRoot: () => getOptionalRuntimeBundleStorageRoot(runtimeBundle) }
|
|
9701
|
+
},
|
|
9702
|
+
chat: { service: agentRuntime.service },
|
|
9703
|
+
systemPrompt: { harness },
|
|
9704
|
+
skills: {
|
|
9705
|
+
workspaceRoot,
|
|
9706
|
+
additionalSkillPaths: runtimePi.additionalSkillPaths,
|
|
9707
|
+
piPackages: runtimePi.packages,
|
|
9708
|
+
noSkills: runtimePi.noSkills,
|
|
9709
|
+
getAdditionalSkillPaths: () => [
|
|
9710
|
+
...getRuntimeProvisioning()?.skillPaths ?? [],
|
|
9711
|
+
...opts.pi?.additionalSkillPaths ?? [],
|
|
9712
|
+
...opts.pi?.getHotReloadableResources?.().additionalSkillPaths ?? []
|
|
9713
|
+
],
|
|
9714
|
+
getPiPackages: () => [
|
|
9715
|
+
...opts.pi?.packages ?? [],
|
|
9716
|
+
...opts.pi?.getHotReloadableResources?.().packages ?? []
|
|
9717
|
+
]
|
|
9718
|
+
},
|
|
9719
|
+
catalog: { tools },
|
|
9720
|
+
commands: {
|
|
9721
|
+
harness,
|
|
9722
|
+
defaultSessionId: sessionId,
|
|
9723
|
+
workdir: runtimeBundle.workspace.root,
|
|
9724
|
+
metering: opts.metering
|
|
9725
|
+
},
|
|
9726
|
+
reload: {
|
|
9727
|
+
harness,
|
|
9728
|
+
defaultSessionId: sessionId,
|
|
9729
|
+
beforeReload: opts.beforeReload,
|
|
9730
|
+
onDiagnostics: (diagnostics) => {
|
|
9731
|
+
lastReloadDiagnostics = diagnostics;
|
|
9732
|
+
}
|
|
9733
|
+
},
|
|
9734
|
+
readyStatus: { tracker: readyTracker }
|
|
9735
|
+
};
|
|
9626
9736
|
}
|
|
9627
9737
|
|
|
9628
9738
|
// src/server/http/csp.ts
|
|
@@ -9650,6 +9760,7 @@ function applyCspHeaders(response, opts = {}) {
|
|
|
9650
9760
|
}
|
|
9651
9761
|
|
|
9652
9762
|
// src/server/registerAgentRoutes.ts
|
|
9763
|
+
import { createHash as createHash6 } from "crypto";
|
|
9653
9764
|
import { basename as basename2 } from "path";
|
|
9654
9765
|
import { AuthStorage as AuthStorage2, ModelRegistry as ModelRegistry2 } from "@mariozechner/pi-coding-agent";
|
|
9655
9766
|
|
|
@@ -9795,6 +9906,8 @@ function buildUploadAgentTools(bundle) {
|
|
|
9795
9906
|
// src/server/registerAgentRoutes.ts
|
|
9796
9907
|
var DEFAULT_VERSION2 = "0.1.0-dev";
|
|
9797
9908
|
var DEFAULT_WORKSPACE_ID4 = "default";
|
|
9909
|
+
var PURE_RUNTIME_MODE2 = "none";
|
|
9910
|
+
var MAX_PURE_BINDINGS = 256;
|
|
9798
9911
|
var STANDARD_AGENT_TOOL_NAMES = ["bash", "read", "write", "edit", "find", "grep", "ls"];
|
|
9799
9912
|
function pluginNameFromPath(path4) {
|
|
9800
9913
|
const fileName = basename2(path4);
|
|
@@ -9814,6 +9927,20 @@ function getAvailableModelProviders() {
|
|
|
9814
9927
|
new Set(availableModels.map((model) => model.provider))
|
|
9815
9928
|
).sort((a, b) => a.localeCompare(b));
|
|
9816
9929
|
}
|
|
9930
|
+
function registerAgentCapabilitiesContributor(app, profile) {
|
|
9931
|
+
const host = app;
|
|
9932
|
+
if (typeof host.registerCapabilitiesContributor !== "function") return;
|
|
9933
|
+
host.registerCapabilitiesContributor(
|
|
9934
|
+
"agent",
|
|
9935
|
+
() => ({
|
|
9936
|
+
agent: {
|
|
9937
|
+
runtimeMode: profile.runtimeMode,
|
|
9938
|
+
tools: profile.capabilities.tools,
|
|
9939
|
+
modelProviders: getAvailableModelProviders()
|
|
9940
|
+
}
|
|
9941
|
+
})
|
|
9942
|
+
);
|
|
9943
|
+
}
|
|
9817
9944
|
function getRequestWorkspaceId2(request) {
|
|
9818
9945
|
return request.workspaceContext?.workspaceId ?? DEFAULT_WORKSPACE_ID4;
|
|
9819
9946
|
}
|
|
@@ -9839,6 +9966,14 @@ function normalizeSessionNamespace(value) {
|
|
|
9839
9966
|
const trimmed = value.trim();
|
|
9840
9967
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
9841
9968
|
}
|
|
9969
|
+
function pureSessionNamespaceFromWorkspaceId(workspaceId) {
|
|
9970
|
+
const digest = createHash6("sha256").update(workspaceId).digest("hex").slice(0, 16);
|
|
9971
|
+
return `workspace-${digest}`;
|
|
9972
|
+
}
|
|
9973
|
+
function pureSessionNamespaceFromScope(parts) {
|
|
9974
|
+
const digest = createHash6("sha256").update(JSON.stringify(parts)).digest("hex").slice(0, 16);
|
|
9975
|
+
return `scope-${digest}`;
|
|
9976
|
+
}
|
|
9842
9977
|
function getRequestAuthSubject2(request) {
|
|
9843
9978
|
const userId = request?.user?.id;
|
|
9844
9979
|
if (typeof userId === "string" && userId.trim()) return userId.trim();
|
|
@@ -9895,10 +10030,14 @@ function createRuntimeReadinessCheck(workspaceId, getRuntimeDependencies) {
|
|
|
9895
10030
|
};
|
|
9896
10031
|
}
|
|
9897
10032
|
var registerAgentRoutes = async (app, opts) => {
|
|
9898
|
-
const workspaceRoot = opts.workspaceRoot ?? process.cwd();
|
|
9899
10033
|
const sessionId = opts.sessionId ?? DEFAULT_WORKSPACE_ID4;
|
|
9900
|
-
const templatePath = opts.templatePath ?? getEnv("BORING_AGENT_TEMPLATE_PATH");
|
|
9901
10034
|
const resolvedMode = opts.runtimeModeAdapter?.id ?? opts.mode ?? autoDetectMode();
|
|
10035
|
+
if (!opts.runtimeModeAdapter && resolvedMode === PURE_RUNTIME_MODE2) {
|
|
10036
|
+
await registerPureAgentRoutes(app, opts, sessionId, resolvedMode);
|
|
10037
|
+
return;
|
|
10038
|
+
}
|
|
10039
|
+
const workspaceRoot = opts.workspaceRoot ?? process.cwd();
|
|
10040
|
+
const templatePath = opts.templatePath ?? getEnv("BORING_AGENT_TEMPLATE_PATH");
|
|
9902
10041
|
const modeAdapter = opts.runtimeModeAdapter ?? resolveMode(resolvedMode, { sandboxHandleStore: opts.sandboxHandleStore });
|
|
9903
10042
|
app.addHook("onClose", async () => {
|
|
9904
10043
|
await modeAdapter.dispose?.();
|
|
@@ -10517,6 +10656,144 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
10517
10656
|
staticBinding ? { tracker: staticBinding.readyTracker } : { getTracker: async (request) => (await getBindingForRequest(request)).readyTracker }
|
|
10518
10657
|
);
|
|
10519
10658
|
};
|
|
10659
|
+
async function registerPureAgentRoutes(app, opts, sessionId, resolvedMode) {
|
|
10660
|
+
const pureBindings = /* @__PURE__ */ new Map();
|
|
10661
|
+
const requestScopedPure = typeof opts.getWorkspaceId === "function" || typeof opts.getWorkspaceRoot === "function" || typeof opts.getExtraTools === "function" || typeof opts.getPi === "function" || typeof opts.getSessionNamespace === "function" || typeof opts.getSystemPromptDynamic === "function";
|
|
10662
|
+
function evictPureBindings() {
|
|
10663
|
+
if (pureBindings.size <= MAX_PURE_BINDINGS) return;
|
|
10664
|
+
const keys = Array.from(pureBindings.keys());
|
|
10665
|
+
for (let i = 0; i < keys.length - MAX_PURE_BINDINGS; i += 1) {
|
|
10666
|
+
const key = keys[i];
|
|
10667
|
+
if (!key) continue;
|
|
10668
|
+
const binding = pureBindings.get(key);
|
|
10669
|
+
pureBindings.delete(key);
|
|
10670
|
+
binding?.then((resolved) => resolved.agent.dispose()).catch(() => {
|
|
10671
|
+
});
|
|
10672
|
+
}
|
|
10673
|
+
}
|
|
10674
|
+
async function createPureBinding(request) {
|
|
10675
|
+
const workspaceId = request ? getRequestWorkspaceId2(request) : sessionId;
|
|
10676
|
+
const workspaceRoot = request && opts.getWorkspaceRoot ? await opts.getWorkspaceRoot(workspaceId, request) : opts.workspaceRoot ?? "";
|
|
10677
|
+
const pi = withPiHarnessDefaults(opts.getPi ? await opts.getPi({ workspaceId, workspaceRoot, request }) : opts.pi);
|
|
10678
|
+
const configuredSessionNamespace = normalizeSessionNamespace(opts.getSessionNamespace ? await opts.getSessionNamespace({ workspaceId, workspaceRoot, request }) : opts.sessionNamespace);
|
|
10679
|
+
const authSubject = opts.getExtraTools ? getRequestAuthSubject2(request) : void 0;
|
|
10680
|
+
const pureScopeNamespace = opts.getWorkspaceId ? pureSessionNamespaceFromWorkspaceId(workspaceId) : requestScopedPure ? pureSessionNamespaceFromScope([resolvedMode, workspaceId, workspaceRoot, pi, authSubject ?? null]) : void 0;
|
|
10681
|
+
const sessionNamespace = configuredSessionNamespace && pureScopeNamespace ? `${configuredSessionNamespace}-${pureScopeNamespace}` : configuredSessionNamespace ?? pureScopeNamespace;
|
|
10682
|
+
const key = JSON.stringify([
|
|
10683
|
+
resolvedMode,
|
|
10684
|
+
workspaceId,
|
|
10685
|
+
workspaceRoot,
|
|
10686
|
+
pi,
|
|
10687
|
+
sessionNamespace ?? null,
|
|
10688
|
+
authSubject ?? null
|
|
10689
|
+
]);
|
|
10690
|
+
const existing = pureBindings.get(key);
|
|
10691
|
+
if (existing) return existing;
|
|
10692
|
+
const bindingPromise = (async () => {
|
|
10693
|
+
const scopedExtraTools = opts.getExtraTools ? await opts.getExtraTools({
|
|
10694
|
+
workspaceId,
|
|
10695
|
+
workspaceRoot,
|
|
10696
|
+
runtimeMode: resolvedMode,
|
|
10697
|
+
workspaceFsCapability: "none",
|
|
10698
|
+
authSubject
|
|
10699
|
+
}) : [];
|
|
10700
|
+
const tools = mergeTools({
|
|
10701
|
+
standardTools: [],
|
|
10702
|
+
extraTools: [...opts.extraTools ?? [], ...scopedExtraTools],
|
|
10703
|
+
logger: app.log
|
|
10704
|
+
});
|
|
10705
|
+
const baseHarnessFactory = opts.harnessFactory ?? ((input) => createPiCodingAgentHarness({
|
|
10706
|
+
...input,
|
|
10707
|
+
pi
|
|
10708
|
+
}));
|
|
10709
|
+
const systemPromptDynamic = opts.getSystemPromptDynamic ? () => opts.getSystemPromptDynamic?.({ workspaceId, workspaceRoot }) : opts.systemPromptDynamic;
|
|
10710
|
+
const harnessFactory = ((input) => baseHarnessFactory({
|
|
10711
|
+
...input,
|
|
10712
|
+
sessionNamespace,
|
|
10713
|
+
sessionRoot: opts.sessionRoot,
|
|
10714
|
+
sessionDir: input.sessionDir
|
|
10715
|
+
}));
|
|
10716
|
+
const coreAgent = createAgentRuntimeBridge({
|
|
10717
|
+
runtime: "none",
|
|
10718
|
+
tools,
|
|
10719
|
+
harnessFactory,
|
|
10720
|
+
systemPromptAppend: opts.systemPromptAppend,
|
|
10721
|
+
systemPromptDynamic,
|
|
10722
|
+
telemetry: opts.telemetry,
|
|
10723
|
+
metering: opts.metering,
|
|
10724
|
+
sessionStorageRoot: opts.sessionRoot
|
|
10725
|
+
});
|
|
10726
|
+
const agentRuntime = await coreAgent.getRuntime();
|
|
10727
|
+
return {
|
|
10728
|
+
agent: coreAgent.agent,
|
|
10729
|
+
tools,
|
|
10730
|
+
readyTracker: new ReadyStatusTracker({ sandboxReady: true, harnessReady: true }),
|
|
10731
|
+
piChatService: agentRuntime.service
|
|
10732
|
+
};
|
|
10733
|
+
})();
|
|
10734
|
+
pureBindings.set(key, bindingPromise);
|
|
10735
|
+
bindingPromise.catch(() => {
|
|
10736
|
+
if (pureBindings.get(key) === bindingPromise) pureBindings.delete(key);
|
|
10737
|
+
});
|
|
10738
|
+
evictPureBindings();
|
|
10739
|
+
return bindingPromise;
|
|
10740
|
+
}
|
|
10741
|
+
const staticBinding = requestScopedPure ? void 0 : await createPureBinding();
|
|
10742
|
+
const profile = {
|
|
10743
|
+
runtimeMode: resolvedMode,
|
|
10744
|
+
capabilities: {
|
|
10745
|
+
tools: staticBinding ? toolNames(staticBinding.tools) : toolNames(opts.extraTools ?? [])
|
|
10746
|
+
},
|
|
10747
|
+
sessionChangesTracker: new InMemorySessionChangesTracker(),
|
|
10748
|
+
health: {
|
|
10749
|
+
register: opts.registerHealthRoute ?? true,
|
|
10750
|
+
version: opts.version ?? DEFAULT_VERSION2,
|
|
10751
|
+
getReadiness: () => ({ sandboxReady: true, harnessReady: true })
|
|
10752
|
+
},
|
|
10753
|
+
chat: {
|
|
10754
|
+
...staticBinding ? { service: staticBinding.piChatService } : { getService: async (request) => (await createPureBinding(request)).piChatService },
|
|
10755
|
+
defaultWorkspaceId: false
|
|
10756
|
+
},
|
|
10757
|
+
models: { filterModels: opts.filterModels },
|
|
10758
|
+
catalog: staticBinding ? { tools: staticBinding.tools } : { getTools: async (request) => (await createPureBinding(request)).tools },
|
|
10759
|
+
readyStatus: staticBinding ? { tracker: staticBinding.readyTracker } : { getTracker: async (request) => (await createPureBinding(request)).readyTracker },
|
|
10760
|
+
dispose: async () => {
|
|
10761
|
+
const bindings = await Promise.allSettled(pureBindings.values());
|
|
10762
|
+
await Promise.all(bindings.filter((result) => result.status === "fulfilled").map((result) => result.value.agent.dispose()));
|
|
10763
|
+
},
|
|
10764
|
+
beforeRegister: (profileApp) => {
|
|
10765
|
+
profileApp.addHook("onRequest", async (request, reply) => {
|
|
10766
|
+
const user = request.user;
|
|
10767
|
+
let workspaceId = DEFAULT_WORKSPACE_ID4;
|
|
10768
|
+
if (opts.getWorkspaceId && !isWorkspaceAgnosticAgentRequest(request, {
|
|
10769
|
+
readyStatusWorkspaceScoped: requestScopedPure,
|
|
10770
|
+
modelsWorkspaceScoped: Boolean(opts.filterModels)
|
|
10771
|
+
})) {
|
|
10772
|
+
try {
|
|
10773
|
+
workspaceId = (await opts.getWorkspaceId(request)).trim();
|
|
10774
|
+
} catch (error) {
|
|
10775
|
+
const statusCode = typeof error?.statusCode === "number" ? error.statusCode : 400;
|
|
10776
|
+
const message = statusCode >= 500 ? "workspace scope failed" : error instanceof Error ? error.message : "workspace id is required";
|
|
10777
|
+
return reply.code(statusCode).send({
|
|
10778
|
+
error: { code: ErrorCode.enum.WORKSPACE_UNINITIALIZED, message }
|
|
10779
|
+
});
|
|
10780
|
+
}
|
|
10781
|
+
if (workspaceId.length === 0) {
|
|
10782
|
+
return reply.code(400).send({
|
|
10783
|
+
error: {
|
|
10784
|
+
code: ErrorCode.enum.WORKSPACE_UNINITIALIZED,
|
|
10785
|
+
message: "workspace id is required"
|
|
10786
|
+
}
|
|
10787
|
+
});
|
|
10788
|
+
}
|
|
10789
|
+
}
|
|
10790
|
+
request.workspaceContext = { workspaceId, authenticated: !!user };
|
|
10791
|
+
});
|
|
10792
|
+
}
|
|
10793
|
+
};
|
|
10794
|
+
registerAgentCapabilitiesContributor(app, profile);
|
|
10795
|
+
await registerAgentRouteBindingProfile(app, profile);
|
|
10796
|
+
}
|
|
10520
10797
|
export {
|
|
10521
10798
|
FileHandleStore,
|
|
10522
10799
|
MANAGED_AGENT_MCP_DELIVERY_RULE,
|