@hachej/boring-agent 0.1.75 → 0.1.77
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-3WWLQAJB.js} +52 -13
- package/dist/{chunk-XZKU7FBV.js → chunk-6AEK34XU.js} +2 -0
- package/dist/{chunk-4LXA7OOV.js → chunk-ORURYKNY.js} +1 -1
- package/dist/{chunk-FDGWCMJD.js → chunk-TS3QGFKK.js} +3 -2
- 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-RZUU6MJQ.js} +2 -2
- package/dist/front/index.d.ts +2 -3
- package/dist/front/index.js +2 -2
- package/dist/{harness-Dwxh6USt.d.ts → harness-OsJBlx4u.d.ts} +10 -2
- package/dist/piChatCommand-BuWXytap.d.ts +60 -0
- package/dist/server/index.d.ts +28 -6
- package/dist/server/index.js +714 -287
- package/dist/session-FUiMWsyX.d.ts +297 -0
- package/dist/shared/index.d.ts +25 -26
- package/dist/shared/index.js +7 -3
- package/dist/{agentPluginEvents-CqLTEdG2.d.ts → workspaceAgentDispatcher-Cl3EBveh.d.ts} +15 -1
- package/docs/API.md +27 -0
- package/docs/ERROR_CODES.md +2 -0
- package/docs/runtime.md +12 -0
- package/package.json +2 -2
- package/dist/chatSubmitPayload-DwOHyiqR.d.ts +0 -22
- package/dist/piChatCommand-CSepU1NX.d.ts +0 -40
- package/dist/session-DKMQNtNG.d.ts +0 -297
package/dist/server/index.js
CHANGED
|
@@ -7,16 +7,18 @@ import {
|
|
|
7
7
|
createAgent,
|
|
8
8
|
createAgentRuntimeBridge,
|
|
9
9
|
normalizeMeteringUsage
|
|
10
|
-
} from "../chunk-
|
|
11
|
-
import "../chunk-
|
|
10
|
+
} from "../chunk-3WWLQAJB.js";
|
|
11
|
+
import "../chunk-ZUEITFIJ.js";
|
|
12
12
|
import {
|
|
13
|
+
CommandReceiptSchema,
|
|
13
14
|
FollowUpPayloadSchema,
|
|
14
15
|
InterruptPayloadSchema,
|
|
15
16
|
PiChatSnapshotSchema,
|
|
16
17
|
PromptPayloadSchema,
|
|
17
18
|
QueueClearPayloadSchema,
|
|
18
|
-
StopPayloadSchema
|
|
19
|
-
|
|
19
|
+
StopPayloadSchema,
|
|
20
|
+
StopReceiptSchema
|
|
21
|
+
} from "../chunk-ORURYKNY.js";
|
|
20
22
|
import {
|
|
21
23
|
PI_PACKAGE_RESOURCE_FILTERS,
|
|
22
24
|
PiSessionStore,
|
|
@@ -31,7 +33,7 @@ import {
|
|
|
31
33
|
registerConfiguredModelProviders,
|
|
32
34
|
setEnvDefault,
|
|
33
35
|
withPiHarnessDefaults
|
|
34
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-TS3QGFKK.js";
|
|
35
37
|
import {
|
|
36
38
|
safeCapture
|
|
37
39
|
} from "../chunk-AQBXNPMD.js";
|
|
@@ -40,7 +42,7 @@ import {
|
|
|
40
42
|
} from "../chunk-AJZHR626.js";
|
|
41
43
|
import {
|
|
42
44
|
ErrorCode
|
|
43
|
-
} from "../chunk-
|
|
45
|
+
} from "../chunk-6AEK34XU.js";
|
|
44
46
|
|
|
45
47
|
// src/server/sandbox/direct/createDirectSandbox.ts
|
|
46
48
|
import { spawn } from "child_process";
|
|
@@ -180,7 +182,7 @@ function createDirectSandbox(opts = {}) {
|
|
|
180
182
|
const maxOutputBytes = opts2?.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
181
183
|
const workspaceRoot = runtimeContext.runtimeCwd;
|
|
182
184
|
const cwd = opts2?.cwd ?? workspaceRoot;
|
|
183
|
-
return await new Promise((
|
|
185
|
+
return await new Promise((resolve11, reject) => {
|
|
184
186
|
const child = spawn(cmd, {
|
|
185
187
|
cwd,
|
|
186
188
|
env: withWorkspacePythonEnv({ workspaceRoot, env: opts2?.env, preserveHostHome: true }),
|
|
@@ -209,7 +211,7 @@ function createDirectSandbox(opts = {}) {
|
|
|
209
211
|
if (settled) return;
|
|
210
212
|
settled = true;
|
|
211
213
|
cleanup();
|
|
212
|
-
|
|
214
|
+
resolve11({
|
|
213
215
|
stdout: new Uint8Array(Buffer.concat(stdoutChunks)),
|
|
214
216
|
stderr: new Uint8Array(Buffer.concat(stderrChunks)),
|
|
215
217
|
exitCode: typeof exitCode === "number" ? exitCode : timedOut ? 124 : 1,
|
|
@@ -558,9 +560,9 @@ function createNodeWatcher(root) {
|
|
|
558
560
|
}
|
|
559
561
|
startFsw();
|
|
560
562
|
if (fsw) {
|
|
561
|
-
await new Promise((
|
|
562
|
-
fsw.once("ready",
|
|
563
|
-
fsw.once("error", () =>
|
|
563
|
+
await new Promise((resolve11) => {
|
|
564
|
+
fsw.once("ready", resolve11);
|
|
565
|
+
fsw.once("error", () => resolve11());
|
|
564
566
|
});
|
|
565
567
|
}
|
|
566
568
|
if (closed) return { ok: false, reason: "closed" };
|
|
@@ -969,7 +971,7 @@ function createBwrapSandbox(opts = {}) {
|
|
|
969
971
|
...withSandboxCwd(baseArgs, sandboxCwd),
|
|
970
972
|
...buildCommandArgs(cmd, sandboxOptions.resourceLimits)
|
|
971
973
|
];
|
|
972
|
-
return await new Promise((
|
|
974
|
+
return await new Promise((resolve11, reject) => {
|
|
973
975
|
const child = spawn2("bwrap", args, {
|
|
974
976
|
env: {
|
|
975
977
|
...withWorkspacePythonEnv({ workspaceRoot, env: opts2?.env, sandboxRoot: SANDBOX_HOME2 }),
|
|
@@ -999,7 +1001,7 @@ function createBwrapSandbox(opts = {}) {
|
|
|
999
1001
|
if (settled) return;
|
|
1000
1002
|
settled = true;
|
|
1001
1003
|
cleanup();
|
|
1002
|
-
|
|
1004
|
+
resolve11({
|
|
1003
1005
|
stdout: new Uint8Array(Buffer.concat(stdoutChunks)),
|
|
1004
1006
|
stderr: new Uint8Array(Buffer.concat(stderrChunks)),
|
|
1005
1007
|
exitCode: typeof exitCode === "number" ? exitCode : timedOut ? 124 : 1,
|
|
@@ -5242,11 +5244,11 @@ async function buildTarGz(files) {
|
|
|
5242
5244
|
}
|
|
5243
5245
|
chunks.push(Buffer.alloc(1024));
|
|
5244
5246
|
const tarBuffer = Buffer.concat(chunks);
|
|
5245
|
-
return new Promise((
|
|
5247
|
+
return new Promise((resolve11, reject) => {
|
|
5246
5248
|
const gzip = createGzip({ level: 6 });
|
|
5247
5249
|
const gzChunks = [];
|
|
5248
5250
|
gzip.on("data", (chunk) => gzChunks.push(chunk));
|
|
5249
|
-
gzip.on("end", () =>
|
|
5251
|
+
gzip.on("end", () => resolve11(Buffer.concat(gzChunks)));
|
|
5250
5252
|
gzip.on("error", reject);
|
|
5251
5253
|
Readable.from(tarBuffer).pipe(gzip);
|
|
5252
5254
|
});
|
|
@@ -6466,13 +6468,13 @@ function resultTool(result) {
|
|
|
6466
6468
|
};
|
|
6467
6469
|
}
|
|
6468
6470
|
function deferred() {
|
|
6469
|
-
let
|
|
6471
|
+
let resolve11;
|
|
6470
6472
|
let reject;
|
|
6471
6473
|
const promise = new Promise((res, rej) => {
|
|
6472
|
-
|
|
6474
|
+
resolve11 = res;
|
|
6473
6475
|
reject = rej;
|
|
6474
6476
|
});
|
|
6475
|
-
return { promise, resolve:
|
|
6477
|
+
return { promise, resolve: resolve11, reject };
|
|
6476
6478
|
}
|
|
6477
6479
|
function errorTool(error) {
|
|
6478
6480
|
const safe = safeError(error);
|
|
@@ -7983,33 +7985,182 @@ function buildHarnessAgentTools(bundle, runtime) {
|
|
|
7983
7985
|
return tools;
|
|
7984
7986
|
}
|
|
7985
7987
|
|
|
7986
|
-
// src/server/http/
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7988
|
+
// src/server/http/sessionChangesTracker.ts
|
|
7989
|
+
var InMemorySessionChangesTracker = class _InMemorySessionChangesTracker {
|
|
7990
|
+
static MAX_CHANGES_PER_SESSION = 1e3;
|
|
7991
|
+
bySession = /* @__PURE__ */ new Map();
|
|
7992
|
+
record(sessionId, change) {
|
|
7993
|
+
const existing = this.bySession.get(sessionId);
|
|
7994
|
+
if (existing) {
|
|
7995
|
+
existing.push(change);
|
|
7996
|
+
if (existing.length > _InMemorySessionChangesTracker.MAX_CHANGES_PER_SESSION) {
|
|
7997
|
+
existing.splice(
|
|
7998
|
+
0,
|
|
7999
|
+
existing.length - _InMemorySessionChangesTracker.MAX_CHANGES_PER_SESSION
|
|
8000
|
+
);
|
|
8001
|
+
}
|
|
8002
|
+
return;
|
|
8003
|
+
}
|
|
8004
|
+
this.bySession.set(sessionId, [change]);
|
|
8005
|
+
}
|
|
8006
|
+
list(sessionId) {
|
|
8007
|
+
return [...this.bySession.get(sessionId) ?? []];
|
|
8008
|
+
}
|
|
8009
|
+
clear(sessionId) {
|
|
8010
|
+
this.bySession.delete(sessionId);
|
|
8011
|
+
}
|
|
8012
|
+
};
|
|
8013
|
+
|
|
8014
|
+
// src/server/runtime/readyStatus.ts
|
|
8015
|
+
function defaultCapabilities(sandboxReady, harnessReady) {
|
|
8016
|
+
return {
|
|
8017
|
+
chat: { state: harnessReady ? "ready" : "preparing" },
|
|
8018
|
+
workspace: { state: sandboxReady ? "ready" : "preparing" },
|
|
8019
|
+
runtimeDependencies: { state: "ready" }
|
|
8020
|
+
};
|
|
8021
|
+
}
|
|
8022
|
+
var ReadyStatusTracker = class {
|
|
8023
|
+
_sandboxReady;
|
|
8024
|
+
_harnessReady;
|
|
8025
|
+
_degradedReason;
|
|
8026
|
+
_capabilities;
|
|
8027
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
8028
|
+
constructor(opts) {
|
|
8029
|
+
this._sandboxReady = opts?.sandboxReady ?? false;
|
|
8030
|
+
this._harnessReady = opts?.harnessReady ?? false;
|
|
8031
|
+
this._capabilities = {
|
|
8032
|
+
...defaultCapabilities(this._sandboxReady, this._harnessReady),
|
|
8033
|
+
...opts?.capabilities ?? {}
|
|
8034
|
+
};
|
|
8035
|
+
}
|
|
8036
|
+
get state() {
|
|
8037
|
+
if (this._degradedReason) return "degraded";
|
|
8038
|
+
return this._sandboxReady && this._harnessReady ? "ready" : "provisioning";
|
|
8039
|
+
}
|
|
8040
|
+
isReady() {
|
|
8041
|
+
return this.state === "ready";
|
|
8042
|
+
}
|
|
8043
|
+
getReadiness() {
|
|
7990
8044
|
return {
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
8045
|
+
sandboxReady: this._sandboxReady,
|
|
8046
|
+
harnessReady: this._harnessReady,
|
|
8047
|
+
capabilities: this.getCapabilities(),
|
|
8048
|
+
degradedReason: this._degradedReason
|
|
7994
8049
|
};
|
|
7995
|
-
}
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8050
|
+
}
|
|
8051
|
+
getCapabilities() {
|
|
8052
|
+
return {
|
|
8053
|
+
chat: { ...this._capabilities.chat },
|
|
8054
|
+
workspace: { ...this._capabilities.workspace },
|
|
8055
|
+
runtimeDependencies: { ...this._capabilities.runtimeDependencies }
|
|
8056
|
+
};
|
|
8057
|
+
}
|
|
8058
|
+
updateCapability(name, detail) {
|
|
8059
|
+
this._capabilities = {
|
|
8060
|
+
...this._capabilities,
|
|
8061
|
+
[name]: { ...detail }
|
|
8062
|
+
};
|
|
8063
|
+
this.emit();
|
|
8064
|
+
}
|
|
8065
|
+
updateRuntimeDependencies(detail) {
|
|
8066
|
+
this.updateCapability("runtimeDependencies", detail);
|
|
8067
|
+
}
|
|
8068
|
+
markSandboxReady() {
|
|
8069
|
+
if (this._sandboxReady) return;
|
|
8070
|
+
this._sandboxReady = true;
|
|
8071
|
+
if (this._capabilities.workspace.state === "preparing") {
|
|
8072
|
+
this._capabilities.workspace = { state: "ready" };
|
|
8003
8073
|
}
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8074
|
+
this.emit();
|
|
8075
|
+
}
|
|
8076
|
+
markHarnessReady() {
|
|
8077
|
+
if (this._harnessReady) return;
|
|
8078
|
+
this._harnessReady = true;
|
|
8079
|
+
if (this._capabilities.chat.state === "preparing") {
|
|
8080
|
+
this._capabilities.chat = { state: "ready" };
|
|
8081
|
+
}
|
|
8082
|
+
this.emit();
|
|
8083
|
+
}
|
|
8084
|
+
markDegraded(reason) {
|
|
8085
|
+
this._degradedReason = reason;
|
|
8086
|
+
this.emit();
|
|
8087
|
+
}
|
|
8088
|
+
clearDegraded() {
|
|
8089
|
+
if (!this._degradedReason) return;
|
|
8090
|
+
this._degradedReason = void 0;
|
|
8091
|
+
this.emit();
|
|
8092
|
+
}
|
|
8093
|
+
subscribe(handler) {
|
|
8094
|
+
this.subscribers.add(handler);
|
|
8095
|
+
handler(this.snapshot());
|
|
8096
|
+
return () => {
|
|
8097
|
+
this.subscribers.delete(handler);
|
|
8098
|
+
};
|
|
8099
|
+
}
|
|
8100
|
+
emit() {
|
|
8101
|
+
const event = this.snapshot();
|
|
8102
|
+
for (const h of this.subscribers) h(event);
|
|
8103
|
+
}
|
|
8104
|
+
snapshot() {
|
|
8105
|
+
return {
|
|
8106
|
+
state: this.state,
|
|
8107
|
+
sandboxReady: this._sandboxReady,
|
|
8108
|
+
harnessReady: this._harnessReady,
|
|
8109
|
+
capabilities: this.getCapabilities(),
|
|
8110
|
+
message: this._degradedReason,
|
|
8111
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8112
|
+
};
|
|
8113
|
+
}
|
|
8114
|
+
};
|
|
8115
|
+
|
|
8116
|
+
// src/server/runtime/modeReadiness.ts
|
|
8117
|
+
function createRuntimeReadyStatusTracker(modeAdapter, opts) {
|
|
8118
|
+
const readiness = modeAdapter.readiness;
|
|
8119
|
+
const tracker = new ReadyStatusTracker({
|
|
8120
|
+
sandboxReady: readiness?.initialSandboxReady ?? true,
|
|
8121
|
+
harnessReady: opts.harnessReady,
|
|
8122
|
+
capabilities: {
|
|
8123
|
+
...readiness?.initialWorkspaceReadiness ? { workspace: readiness.initialWorkspaceReadiness } : {},
|
|
8124
|
+
...opts.capabilities ?? {}
|
|
8009
8125
|
}
|
|
8010
|
-
return { status: "ready" };
|
|
8011
8126
|
});
|
|
8012
|
-
|
|
8127
|
+
readiness?.onTrackerCreated?.(tracker);
|
|
8128
|
+
return tracker;
|
|
8129
|
+
}
|
|
8130
|
+
|
|
8131
|
+
// src/server/tools/pluginDiagnostics.ts
|
|
8132
|
+
function createPluginDiagnosticsTool(deps) {
|
|
8133
|
+
return {
|
|
8134
|
+
name: "plugin_diagnostics",
|
|
8135
|
+
description: [
|
|
8136
|
+
"Return current plugin/skill loading errors plus the diagnostics from the",
|
|
8137
|
+
"last /reload. Call this after asking the user to run /reload \u2014 or whenever a",
|
|
8138
|
+
"plugin or skill you expected does not appear to be loaded \u2014 then fix the",
|
|
8139
|
+
"reported error and ask the user to /reload again. Returns a JSON object with",
|
|
8140
|
+
"lastReloadDiagnostics, resourceDiagnostics, and pluginErrors arrays; an empty",
|
|
8141
|
+
"result means no load errors were detected."
|
|
8142
|
+
].join(" "),
|
|
8143
|
+
parameters: {
|
|
8144
|
+
type: "object",
|
|
8145
|
+
properties: {},
|
|
8146
|
+
additionalProperties: false
|
|
8147
|
+
},
|
|
8148
|
+
async execute(_params, ctx) {
|
|
8149
|
+
const harness = deps.getHarness();
|
|
8150
|
+
const resourceDiagnostics = harness?.getResourceDiagnostics?.(ctx.sessionId ?? "") ?? [];
|
|
8151
|
+
const pluginErrors = deps.getPluginErrors ? await deps.getPluginErrors() : [];
|
|
8152
|
+
const payload = {
|
|
8153
|
+
lastReloadDiagnostics: deps.getLastReloadDiagnostics(),
|
|
8154
|
+
resourceDiagnostics,
|
|
8155
|
+
pluginErrors
|
|
8156
|
+
};
|
|
8157
|
+
return {
|
|
8158
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
8159
|
+
details: payload,
|
|
8160
|
+
isError: false
|
|
8161
|
+
};
|
|
8162
|
+
}
|
|
8163
|
+
};
|
|
8013
8164
|
}
|
|
8014
8165
|
|
|
8015
8166
|
// src/server/http/fsEventBroadcaster.ts
|
|
@@ -8404,12 +8555,20 @@ function modelsRoutes(app, opts, done) {
|
|
|
8404
8555
|
}
|
|
8405
8556
|
|
|
8406
8557
|
// src/server/http/routes/skills.ts
|
|
8558
|
+
import { isAbsolute as isAbsolute9, relative as relative13, resolve as resolve10, sep as sep7 } from "path";
|
|
8407
8559
|
import {
|
|
8408
8560
|
DefaultPackageManager,
|
|
8409
8561
|
getAgentDir,
|
|
8410
8562
|
loadSkills
|
|
8411
8563
|
} from "@mariozechner/pi-coding-agent";
|
|
8412
8564
|
var CACHE_TTL_MS2 = 3e4;
|
|
8565
|
+
function pathForWorkspaceEditor(workspaceRoot, filePath) {
|
|
8566
|
+
const pathWithinWorkspace = relative13(resolve10(workspaceRoot), resolve10(filePath));
|
|
8567
|
+
if (pathWithinWorkspace === "" || pathWithinWorkspace === ".." || pathWithinWorkspace.startsWith(`..${sep7}`) || isAbsolute9(pathWithinWorkspace)) {
|
|
8568
|
+
return filePath;
|
|
8569
|
+
}
|
|
8570
|
+
return pathWithinWorkspace.split(sep7).join("/");
|
|
8571
|
+
}
|
|
8413
8572
|
function skillsRoutes(app, opts, done) {
|
|
8414
8573
|
const cached = /* @__PURE__ */ new Map();
|
|
8415
8574
|
async function resolveSkillsForRequest(request, refresh = false) {
|
|
@@ -8448,7 +8607,7 @@ function skillsRoutes(app, opts, done) {
|
|
|
8448
8607
|
const skills = result.skills.map((s) => ({
|
|
8449
8608
|
name: String(s.name),
|
|
8450
8609
|
description: String(s.description ?? ""),
|
|
8451
|
-
...typeof s.filePath === "string" ? { filePath: s.filePath } : {},
|
|
8610
|
+
...typeof s.filePath === "string" ? { filePath: pathForWorkspaceEditor(workspaceRoot, s.filePath) } : {},
|
|
8452
8611
|
...typeof s.sourceInfo?.scope === "string" ? { source: s.sourceInfo.scope } : {}
|
|
8453
8612
|
}));
|
|
8454
8613
|
const entry = { skills, expiresAt: now + CACHE_TTL_MS2 };
|
|
@@ -8510,7 +8669,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8510
8669
|
try {
|
|
8511
8670
|
const service = await resolveService(opts, request);
|
|
8512
8671
|
if (!service.listSessions) throw unsupportedServiceMethod("list Pi chat sessions");
|
|
8513
|
-
return reply.send(await service.listSessions(getRequestContext(request), sessionListOptions(request)));
|
|
8672
|
+
return reply.send(await service.listSessions(getRequestContext(request, opts), sessionListOptions(request)));
|
|
8514
8673
|
} catch (err) {
|
|
8515
8674
|
return sendRouteError(reply, err, "list pi chat sessions failed");
|
|
8516
8675
|
}
|
|
@@ -8521,7 +8680,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8521
8680
|
try {
|
|
8522
8681
|
const service = await resolveService(opts, request);
|
|
8523
8682
|
if (!service.createSession) throw unsupportedServiceMethod("create Pi chat session");
|
|
8524
|
-
return reply.code(201).send(await service.createSession(getRequestContext(request), body));
|
|
8683
|
+
return reply.code(201).send(await service.createSession(getRequestContext(request, opts), body));
|
|
8525
8684
|
} catch (err) {
|
|
8526
8685
|
return sendRouteError(reply, err, "create pi chat session failed");
|
|
8527
8686
|
}
|
|
@@ -8532,7 +8691,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8532
8691
|
try {
|
|
8533
8692
|
const service = await resolveService(opts, request);
|
|
8534
8693
|
if (!service.deleteSession) throw unsupportedServiceMethod("delete Pi chat session");
|
|
8535
|
-
await service.deleteSession(getRequestContext(request), params.sessionId);
|
|
8694
|
+
await service.deleteSession(getRequestContext(request, opts), params.sessionId);
|
|
8536
8695
|
return reply.code(204).send();
|
|
8537
8696
|
} catch (err) {
|
|
8538
8697
|
return sendRouteError(reply, err, "delete pi chat session failed");
|
|
@@ -8543,7 +8702,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8543
8702
|
if (!params) return;
|
|
8544
8703
|
try {
|
|
8545
8704
|
const service = await resolveService(opts, request);
|
|
8546
|
-
const snapshot = await service.readState(getRequestContext(request), params.sessionId);
|
|
8705
|
+
const snapshot = await service.readState(getRequestContext(request, opts), params.sessionId);
|
|
8547
8706
|
return reply.send(PiChatSnapshotSchema.parse(snapshot));
|
|
8548
8707
|
} catch (err) {
|
|
8549
8708
|
return sendRouteError(reply, err, "read pi chat state failed");
|
|
@@ -8564,7 +8723,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8564
8723
|
};
|
|
8565
8724
|
try {
|
|
8566
8725
|
const service = await resolveService(opts, request);
|
|
8567
|
-
const result = await service.subscribe(getRequestContext(request), params.sessionId, query.cursor, writeFrame);
|
|
8726
|
+
const result = await service.subscribe(getRequestContext(request, opts), params.sessionId, query.cursor, writeFrame);
|
|
8568
8727
|
if (result.type !== "ok") {
|
|
8569
8728
|
return sendReplayRangeError(reply, result);
|
|
8570
8729
|
}
|
|
@@ -8598,7 +8757,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8598
8757
|
if (!body) return;
|
|
8599
8758
|
try {
|
|
8600
8759
|
const service = await resolveService(opts, request);
|
|
8601
|
-
const receipt = await service.prompt(getRequestContext(request), params.sessionId, body);
|
|
8760
|
+
const receipt = await service.prompt(getRequestContext(request, opts), params.sessionId, body);
|
|
8602
8761
|
return reply.code(202).send(receipt);
|
|
8603
8762
|
} catch (err) {
|
|
8604
8763
|
return sendRouteError(reply, err, "prompt rejected");
|
|
@@ -8611,7 +8770,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8611
8770
|
if (!body) return;
|
|
8612
8771
|
try {
|
|
8613
8772
|
const service = await resolveService(opts, request);
|
|
8614
|
-
const receipt = await service.followUp(getRequestContext(request), params.sessionId, body);
|
|
8773
|
+
const receipt = await service.followUp(getRequestContext(request, opts), params.sessionId, body);
|
|
8615
8774
|
return reply.code(202).send(receipt);
|
|
8616
8775
|
} catch (err) {
|
|
8617
8776
|
return sendRouteError(reply, err, "follow-up rejected");
|
|
@@ -8624,7 +8783,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8624
8783
|
if (!body) return;
|
|
8625
8784
|
try {
|
|
8626
8785
|
const service = await resolveService(opts, request);
|
|
8627
|
-
const receipt = await service.clearQueue(getRequestContext(request), params.sessionId, body);
|
|
8786
|
+
const receipt = await service.clearQueue(getRequestContext(request, opts), params.sessionId, body);
|
|
8628
8787
|
return reply.code(202).send(receipt);
|
|
8629
8788
|
} catch (err) {
|
|
8630
8789
|
return sendRouteError(reply, err, "queue clear rejected");
|
|
@@ -8637,7 +8796,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8637
8796
|
if (!body) return;
|
|
8638
8797
|
try {
|
|
8639
8798
|
const service = await resolveService(opts, request);
|
|
8640
|
-
const receipt = await service.interrupt(getRequestContext(request), params.sessionId, body);
|
|
8799
|
+
const receipt = await service.interrupt(getRequestContext(request, opts), params.sessionId, body);
|
|
8641
8800
|
return reply.code(202).send(receipt);
|
|
8642
8801
|
} catch (err) {
|
|
8643
8802
|
return sendRouteError(reply, err, "interrupt rejected");
|
|
@@ -8650,7 +8809,7 @@ function piChatRoutes(app, opts, done) {
|
|
|
8650
8809
|
if (!body) return;
|
|
8651
8810
|
try {
|
|
8652
8811
|
const service = await resolveService(opts, request);
|
|
8653
|
-
const receipt = await service.stop(getRequestContext(request), params.sessionId, body);
|
|
8812
|
+
const receipt = await service.stop(getRequestContext(request, opts), params.sessionId, body);
|
|
8654
8813
|
return reply.code(202).send(receipt);
|
|
8655
8814
|
} catch (err) {
|
|
8656
8815
|
return sendRouteError(reply, err, "stop rejected");
|
|
@@ -8710,13 +8869,14 @@ async function resolveService(opts, request) {
|
|
|
8710
8869
|
}
|
|
8711
8870
|
return service;
|
|
8712
8871
|
}
|
|
8713
|
-
function getRequestContext(request) {
|
|
8872
|
+
function getRequestContext(request, opts) {
|
|
8714
8873
|
const storageScopeHeader = request.headers["x-boring-storage-scope"];
|
|
8715
8874
|
const user = request.user;
|
|
8716
8875
|
const authSubject = user?.id;
|
|
8717
8876
|
const authEmail = user?.email;
|
|
8877
|
+
const workspaceId = opts.defaultWorkspaceId === false ? void 0 : request.workspaceContext?.workspaceId ?? opts.defaultWorkspaceId ?? DEFAULT_WORKSPACE_ID2;
|
|
8718
8878
|
return {
|
|
8719
|
-
workspaceId
|
|
8879
|
+
workspaceId,
|
|
8720
8880
|
storageScope: typeof storageScopeHeader === "string" && storageScopeHeader.length > 0 ? storageScopeHeader : void 0,
|
|
8721
8881
|
authSubject: typeof authSubject === "string" && authSubject.length > 0 ? authSubject : void 0,
|
|
8722
8882
|
authEmail: typeof authEmail === "string" && authEmail.length > 0 ? authEmail : void 0,
|
|
@@ -8809,32 +8969,6 @@ function systemPromptRoutes(app, opts, done) {
|
|
|
8809
8969
|
done();
|
|
8810
8970
|
}
|
|
8811
8971
|
|
|
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
8972
|
// src/server/http/routes/sessionChanges.ts
|
|
8839
8973
|
function requireSessionId(value, reply) {
|
|
8840
8974
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -9163,7 +9297,7 @@ function searchRoutes(app, opts, done) {
|
|
|
9163
9297
|
}
|
|
9164
9298
|
|
|
9165
9299
|
// src/server/git/gitFileUrl.ts
|
|
9166
|
-
import { dirname as dirname12, relative as
|
|
9300
|
+
import { dirname as dirname12, relative as relative14 } from "path";
|
|
9167
9301
|
import { execFile as execFile3 } from "child_process";
|
|
9168
9302
|
import { promisify as promisify3 } from "util";
|
|
9169
9303
|
|
|
@@ -9246,7 +9380,7 @@ async function resolveGitFileUrl(workspaceRoot, path4) {
|
|
|
9246
9380
|
commitSha = null;
|
|
9247
9381
|
}
|
|
9248
9382
|
}
|
|
9249
|
-
const repoRelativePath =
|
|
9383
|
+
const repoRelativePath = relative14(repoRoot, absolutePath).replace(/\\/g, "/");
|
|
9250
9384
|
const url = buildGitFileUrl({ remoteUrl, repoRelativePath, branch, commitSha });
|
|
9251
9385
|
if (!url) {
|
|
9252
9386
|
return disabled("Only GitHub SSH/HTTPS remotes are supported right now.");
|
|
@@ -9298,167 +9432,225 @@ function gitRoutes(app, opts, done) {
|
|
|
9298
9432
|
done();
|
|
9299
9433
|
}
|
|
9300
9434
|
|
|
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() {
|
|
9435
|
+
// src/server/http/routes/health.ts
|
|
9436
|
+
function healthRoutes(app, opts, done) {
|
|
9437
|
+
const startTime = Date.now();
|
|
9438
|
+
app.get("/health", async () => {
|
|
9331
9439
|
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 }
|
|
9440
|
+
status: "ok",
|
|
9441
|
+
version: opts.version,
|
|
9442
|
+
uptime: Math.floor((Date.now() - startTime) / 1e3)
|
|
9349
9443
|
};
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
if (this._capabilities.workspace.state === "preparing") {
|
|
9359
|
-
this._capabilities.workspace = { state: "ready" };
|
|
9444
|
+
});
|
|
9445
|
+
app.get("/ready", async (_request, reply) => {
|
|
9446
|
+
const state = opts.getReadiness();
|
|
9447
|
+
if (state.degradedReason) {
|
|
9448
|
+
return reply.code(503).send({
|
|
9449
|
+
status: "degraded",
|
|
9450
|
+
reason: state.degradedReason
|
|
9451
|
+
});
|
|
9360
9452
|
}
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
if (this._capabilities.chat.state === "preparing") {
|
|
9367
|
-
this._capabilities.chat = { state: "ready" };
|
|
9453
|
+
if (!state.sandboxReady || !state.harnessReady) {
|
|
9454
|
+
return reply.code(503).send({
|
|
9455
|
+
status: "provisioning",
|
|
9456
|
+
retryAfter: 2
|
|
9457
|
+
});
|
|
9368
9458
|
}
|
|
9369
|
-
|
|
9370
|
-
}
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
this.emit();
|
|
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
|
-
};
|
|
9459
|
+
return { status: "ready" };
|
|
9460
|
+
});
|
|
9461
|
+
done();
|
|
9462
|
+
}
|
|
9402
9463
|
|
|
9403
|
-
// src/server/
|
|
9404
|
-
function
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9464
|
+
// src/server/agentRouteBindingProfile.ts
|
|
9465
|
+
function toolNames(tools) {
|
|
9466
|
+
return tools.map((tool) => tool.name);
|
|
9467
|
+
}
|
|
9468
|
+
async function registerAgentRouteBindingProfile(app, profile) {
|
|
9469
|
+
if (profile.dispose) {
|
|
9470
|
+
app.addHook("onClose", async () => {
|
|
9471
|
+
await profile.dispose?.();
|
|
9472
|
+
});
|
|
9473
|
+
}
|
|
9474
|
+
await profile.beforeRegister?.(app);
|
|
9475
|
+
const { register = true, ...health } = profile.health;
|
|
9476
|
+
if (register) await app.register(healthRoutes, health);
|
|
9477
|
+
if (profile.filesystem) {
|
|
9478
|
+
await app.register(fileRoutes, profile.filesystem.file);
|
|
9479
|
+
await app.register(fsEventsRoutes, profile.filesystem.fsEvents);
|
|
9480
|
+
await app.register(treeRoutes, profile.filesystem.tree);
|
|
9481
|
+
await app.register(searchRoutes, profile.filesystem.search);
|
|
9482
|
+
await app.register(gitRoutes, profile.filesystem.git);
|
|
9483
|
+
}
|
|
9484
|
+
await app.register(piChatRoutes, profile.chat);
|
|
9485
|
+
if (profile.systemPrompt) await app.register(systemPromptRoutes, profile.systemPrompt);
|
|
9486
|
+
await app.register(modelsRoutes, profile.models ?? {});
|
|
9487
|
+
if (profile.skills) await app.register(skillsRoutes, profile.skills);
|
|
9488
|
+
await app.register(sessionChangesRoutes, { tracker: profile.sessionChangesTracker });
|
|
9489
|
+
if (profile.reload) {
|
|
9490
|
+
if (typeof profile.reload === "function") {
|
|
9491
|
+
await profile.reload(app);
|
|
9492
|
+
} else {
|
|
9493
|
+
await app.register(reloadRoutes, profile.reload);
|
|
9412
9494
|
}
|
|
9413
|
-
}
|
|
9414
|
-
|
|
9415
|
-
|
|
9495
|
+
}
|
|
9496
|
+
await app.register(catalogRoutes, profile.catalog);
|
|
9497
|
+
if (profile.commands) await app.register(commandsRoutes, profile.commands);
|
|
9498
|
+
await app.register(readyStatusRoutes, profile.readyStatus);
|
|
9416
9499
|
}
|
|
9417
9500
|
|
|
9418
|
-
// src/server/
|
|
9419
|
-
function
|
|
9501
|
+
// src/server/workspaceAgentDispatcher.ts
|
|
9502
|
+
function createBoundWorkspaceAgentDispatcher(agent, ctx) {
|
|
9503
|
+
const boundCtx = normalizeWorkspaceAgentDispatcherContext(ctx);
|
|
9420
9504
|
return {
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
"Return current plugin/skill loading errors plus the diagnostics from the",
|
|
9424
|
-
"last /reload. Call this after asking the user to run /reload \u2014 or whenever a",
|
|
9425
|
-
"plugin or skill you expected does not appear to be loaded \u2014 then fix the",
|
|
9426
|
-
"reported error and ask the user to /reload again. Returns a JSON object with",
|
|
9427
|
-
"lastReloadDiagnostics, resourceDiagnostics, and pluginErrors arrays; an empty",
|
|
9428
|
-
"result means no load errors were detected."
|
|
9429
|
-
].join(" "),
|
|
9430
|
-
parameters: {
|
|
9431
|
-
type: "object",
|
|
9432
|
-
properties: {},
|
|
9433
|
-
additionalProperties: false
|
|
9505
|
+
send(input) {
|
|
9506
|
+
return agent.send({ ...input, ctx: boundCtx });
|
|
9434
9507
|
},
|
|
9435
|
-
async
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
lastReloadDiagnostics: deps.getLastReloadDiagnostics(),
|
|
9441
|
-
resourceDiagnostics,
|
|
9442
|
-
pluginErrors
|
|
9443
|
-
};
|
|
9444
|
-
return {
|
|
9445
|
-
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
9446
|
-
details: payload,
|
|
9447
|
-
isError: false
|
|
9448
|
-
};
|
|
9508
|
+
async interrupt(sessionId) {
|
|
9509
|
+
return parseControlReceipt(await agent.interrupt(sessionId, boundCtx), "interrupt");
|
|
9510
|
+
},
|
|
9511
|
+
async stop(sessionId) {
|
|
9512
|
+
return parseStopReceipt(await agent.stop(sessionId, boundCtx));
|
|
9449
9513
|
}
|
|
9450
9514
|
};
|
|
9451
9515
|
}
|
|
9516
|
+
function assertWorkspaceAgentDispatcherRequestContext(ctx, request) {
|
|
9517
|
+
normalizeWorkspaceAgentDispatcherContext(ctx);
|
|
9518
|
+
if (!request) return;
|
|
9519
|
+
const requestWorkspaceId = request.workspaceContext?.workspaceId?.trim();
|
|
9520
|
+
if (!requestWorkspaceId) return;
|
|
9521
|
+
if (requestWorkspaceId !== ctx.workspaceId.trim()) {
|
|
9522
|
+
throw createWorkspaceAgentDispatcherError(
|
|
9523
|
+
ErrorCode.enum.UNAUTHORIZED,
|
|
9524
|
+
"workspace agent dispatcher context does not match request workspace",
|
|
9525
|
+
401
|
|
9526
|
+
);
|
|
9527
|
+
}
|
|
9528
|
+
}
|
|
9529
|
+
function normalizeWorkspaceAgentDispatcherContext(ctx) {
|
|
9530
|
+
const workspaceId = ctx.workspaceId?.trim();
|
|
9531
|
+
const userId = ctx.userId?.trim();
|
|
9532
|
+
if (!workspaceId) {
|
|
9533
|
+
throw createWorkspaceAgentDispatcherError(
|
|
9534
|
+
ErrorCode.enum.WORKSPACE_UNINITIALIZED,
|
|
9535
|
+
"workspace id is required",
|
|
9536
|
+
400
|
|
9537
|
+
);
|
|
9538
|
+
}
|
|
9539
|
+
if (!userId) {
|
|
9540
|
+
throw createWorkspaceAgentDispatcherError(
|
|
9541
|
+
ErrorCode.enum.UNAUTHORIZED,
|
|
9542
|
+
"user id is required",
|
|
9543
|
+
401
|
|
9544
|
+
);
|
|
9545
|
+
}
|
|
9546
|
+
return { workspaceId, userId };
|
|
9547
|
+
}
|
|
9548
|
+
function parseControlReceipt(receipt, action) {
|
|
9549
|
+
const parsed = CommandReceiptSchema.safeParse(receipt);
|
|
9550
|
+
if (parsed.success) return parsed.data;
|
|
9551
|
+
throw createWorkspaceAgentDispatcherError(
|
|
9552
|
+
ErrorCode.enum.AGENT_CONTROL_RECEIPT_INVALID,
|
|
9553
|
+
`agent ${action} returned a malformed receipt`,
|
|
9554
|
+
500
|
|
9555
|
+
);
|
|
9556
|
+
}
|
|
9557
|
+
function parseStopReceipt(receipt) {
|
|
9558
|
+
const parsed = StopReceiptSchema.safeParse(receipt);
|
|
9559
|
+
if (parsed.success) return parsed.data;
|
|
9560
|
+
throw createWorkspaceAgentDispatcherError(
|
|
9561
|
+
ErrorCode.enum.AGENT_CONTROL_RECEIPT_INVALID,
|
|
9562
|
+
"agent stop returned a malformed receipt",
|
|
9563
|
+
500
|
|
9564
|
+
);
|
|
9565
|
+
}
|
|
9566
|
+
function createWorkspaceAgentDispatcherError(code, message, statusCode) {
|
|
9567
|
+
const error = new Error(message);
|
|
9568
|
+
error.code = code;
|
|
9569
|
+
error.statusCode = statusCode;
|
|
9570
|
+
return error;
|
|
9571
|
+
}
|
|
9452
9572
|
|
|
9453
9573
|
// src/server/createAgentApp.ts
|
|
9454
9574
|
var DEFAULT_VERSION = "0.1.0-dev";
|
|
9455
9575
|
var DEFAULT_SESSION_ID = "default";
|
|
9576
|
+
var PURE_RUNTIME_MODE = "none";
|
|
9577
|
+
function createStaticWorkspaceAgentDispatcherResolver(agent, workspaceId) {
|
|
9578
|
+
return {
|
|
9579
|
+
async resolve(ctx, options) {
|
|
9580
|
+
const boundCtx = normalizeWorkspaceAgentDispatcherContext(ctx);
|
|
9581
|
+
assertWorkspaceAgentDispatcherRequestContext(boundCtx, options?.request);
|
|
9582
|
+
if (boundCtx.workspaceId !== workspaceId) {
|
|
9583
|
+
throw createWorkspaceAgentDispatcherError(
|
|
9584
|
+
ErrorCode.enum.UNAUTHORIZED,
|
|
9585
|
+
"workspace agent dispatcher context does not match bound workspace",
|
|
9586
|
+
401
|
|
9587
|
+
);
|
|
9588
|
+
}
|
|
9589
|
+
return createBoundWorkspaceAgentDispatcher(agent, boundCtx);
|
|
9590
|
+
}
|
|
9591
|
+
};
|
|
9592
|
+
}
|
|
9456
9593
|
async function createAgentApp(opts = {}) {
|
|
9457
|
-
const workspaceRoot = opts.workspaceRoot ?? process.cwd();
|
|
9458
9594
|
const sessionId = opts.sessionId ?? DEFAULT_SESSION_ID;
|
|
9459
|
-
const templatePath = opts.templatePath ?? getEnv("BORING_AGENT_TEMPLATE_PATH");
|
|
9460
9595
|
const app = Fastify({ logger: opts.logger ?? true, bodyLimit: 16 * 1024 * 1024 });
|
|
9461
9596
|
const resolvedMode = opts.runtimeModeAdapter?.id ?? opts.mode ?? autoDetectMode();
|
|
9597
|
+
const profile = !opts.runtimeModeAdapter && resolvedMode === PURE_RUNTIME_MODE ? await createPureAgentAppProfile(opts, sessionId, resolvedMode) : await createWorkspaceAgentAppProfile(opts, sessionId, resolvedMode, app);
|
|
9598
|
+
app.addHook(
|
|
9599
|
+
"onRequest",
|
|
9600
|
+
createAuthMiddleware({
|
|
9601
|
+
authToken: opts.authToken,
|
|
9602
|
+
publicPaths: ["/health", "/ready", "/api/v1/ready-status"]
|
|
9603
|
+
})
|
|
9604
|
+
);
|
|
9605
|
+
await registerAgentRouteBindingProfile(app, profile);
|
|
9606
|
+
return app;
|
|
9607
|
+
}
|
|
9608
|
+
async function createPureAgentAppProfile(opts, sessionId, resolvedMode) {
|
|
9609
|
+
const runtimePi = withPiHarnessDefaults(opts.pi);
|
|
9610
|
+
const baseHarnessFactory = opts.harnessFactory ?? ((input) => createPiCodingAgentHarness({
|
|
9611
|
+
...input,
|
|
9612
|
+
pi: runtimePi
|
|
9613
|
+
}));
|
|
9614
|
+
const harnessFactory = ((input) => baseHarnessFactory({
|
|
9615
|
+
...input,
|
|
9616
|
+
sessionNamespace: opts.sessionNamespace,
|
|
9617
|
+
sessionRoot: opts.sessionRoot,
|
|
9618
|
+
sessionDir: opts.sessionDir ?? input.sessionDir
|
|
9619
|
+
}));
|
|
9620
|
+
const tools = opts.extraTools ?? [];
|
|
9621
|
+
const coreAgent = createAgentRuntimeBridge({
|
|
9622
|
+
runtime: "none",
|
|
9623
|
+
tools,
|
|
9624
|
+
harnessFactory,
|
|
9625
|
+
systemPromptAppend: opts.systemPromptAppend,
|
|
9626
|
+
systemPromptDynamic: opts.systemPromptDynamic,
|
|
9627
|
+
telemetry: opts.telemetry,
|
|
9628
|
+
metering: opts.metering,
|
|
9629
|
+
sessionStorageRoot: opts.sessionRoot
|
|
9630
|
+
});
|
|
9631
|
+
const agentRuntime = await coreAgent.getRuntime();
|
|
9632
|
+
opts.onWorkspaceAgentDispatcher?.(createStaticWorkspaceAgentDispatcherResolver(coreAgent.agent, sessionId));
|
|
9633
|
+
const readyTracker = new ReadyStatusTracker({ sandboxReady: true, harnessReady: true });
|
|
9634
|
+
return {
|
|
9635
|
+
runtimeMode: resolvedMode,
|
|
9636
|
+
capabilities: { tools: toolNames(tools) },
|
|
9637
|
+
sessionChangesTracker: new InMemorySessionChangesTracker(),
|
|
9638
|
+
health: {
|
|
9639
|
+
version: opts.version ?? DEFAULT_VERSION,
|
|
9640
|
+
getReadiness: () => ({ sandboxReady: true, harnessReady: true })
|
|
9641
|
+
},
|
|
9642
|
+
chat: {
|
|
9643
|
+
service: agentRuntime.service,
|
|
9644
|
+
defaultWorkspaceId: false
|
|
9645
|
+
},
|
|
9646
|
+
catalog: { tools },
|
|
9647
|
+
readyStatus: { tracker: readyTracker },
|
|
9648
|
+
dispose: () => coreAgent.agent.dispose()
|
|
9649
|
+
};
|
|
9650
|
+
}
|
|
9651
|
+
async function createWorkspaceAgentAppProfile(opts, sessionId, resolvedMode, app) {
|
|
9652
|
+
const workspaceRoot = opts.workspaceRoot ?? process.cwd();
|
|
9653
|
+
const templatePath = opts.templatePath ?? getEnv("BORING_AGENT_TEMPLATE_PATH");
|
|
9462
9654
|
const modeAdapter = opts.runtimeModeAdapter ?? resolveMode(resolvedMode);
|
|
9463
9655
|
let runtimeBundle = await modeAdapter.create({
|
|
9464
9656
|
workspaceRoot,
|
|
@@ -9536,7 +9728,8 @@ async function createAgentApp(opts = {}) {
|
|
|
9536
9728
|
const harnessFactory = ((input) => baseHarnessFactory({
|
|
9537
9729
|
...input,
|
|
9538
9730
|
sessionNamespace: opts.sessionNamespace,
|
|
9539
|
-
|
|
9731
|
+
sessionRoot: opts.sessionRoot,
|
|
9732
|
+
sessionDir: opts.sessionDir ?? input.sessionDir
|
|
9540
9733
|
}));
|
|
9541
9734
|
const coreAgent = createAgentRuntimeBridge({
|
|
9542
9735
|
runtime: modeAdapter,
|
|
@@ -9546,6 +9739,7 @@ async function createAgentApp(opts = {}) {
|
|
|
9546
9739
|
systemPromptDynamic: opts.systemPromptDynamic,
|
|
9547
9740
|
telemetry: opts.telemetry,
|
|
9548
9741
|
metering: opts.metering,
|
|
9742
|
+
sessionStorageRoot: opts.sessionRoot,
|
|
9549
9743
|
workdir: workspaceRoot
|
|
9550
9744
|
}, {
|
|
9551
9745
|
service: {
|
|
@@ -9554,23 +9748,12 @@ async function createAgentApp(opts = {}) {
|
|
|
9554
9748
|
}
|
|
9555
9749
|
});
|
|
9556
9750
|
const agentRuntime = await coreAgent.getRuntime();
|
|
9751
|
+
opts.onWorkspaceAgentDispatcher?.(createStaticWorkspaceAgentDispatcherResolver(coreAgent.agent, sessionId));
|
|
9557
9752
|
const harness = agentRuntime.harness;
|
|
9558
9753
|
harnessRef = harness;
|
|
9559
|
-
const sessionChangesTracker = new InMemorySessionChangesTracker();
|
|
9560
9754
|
const readyTracker = createRuntimeReadyStatusTracker(modeAdapter, {
|
|
9561
9755
|
harnessReady: true
|
|
9562
9756
|
});
|
|
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
9757
|
const filesystemBindingsForRequest = opts.getFilesystemBindings ? (request) => {
|
|
9575
9758
|
const user = request.user;
|
|
9576
9759
|
return opts.getFilesystemBindings?.({
|
|
@@ -9583,46 +9766,65 @@ async function createAgentApp(opts = {}) {
|
|
|
9583
9766
|
requestId: request.id
|
|
9584
9767
|
});
|
|
9585
9768
|
} : 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
|
-
|
|
9769
|
+
return {
|
|
9770
|
+
runtimeMode: resolvedMode,
|
|
9771
|
+
capabilities: { tools: toolNames(tools) },
|
|
9772
|
+
sessionChangesTracker: new InMemorySessionChangesTracker(),
|
|
9773
|
+
health: {
|
|
9774
|
+
version: opts.version ?? DEFAULT_VERSION,
|
|
9775
|
+
getReadiness: () => readyTracker.getReadiness()
|
|
9776
|
+
},
|
|
9777
|
+
filesystem: {
|
|
9778
|
+
file: {
|
|
9779
|
+
workspace: runtimeBundle.workspace,
|
|
9780
|
+
getFilesystemBindings: filesystemBindingsForRequest,
|
|
9781
|
+
filesystemBindings: runtimeBundle.filesystemBindings
|
|
9782
|
+
},
|
|
9783
|
+
fsEvents: { workspace: runtimeBundle.workspace },
|
|
9784
|
+
tree: {
|
|
9785
|
+
workspace: runtimeBundle.workspace,
|
|
9786
|
+
getFilesystemBindings: filesystemBindingsForRequest,
|
|
9787
|
+
filesystemBindings: runtimeBundle.filesystemBindings
|
|
9788
|
+
},
|
|
9789
|
+
// File search shares the same bound implementation as the model tool.
|
|
9790
|
+
search: { fileSearch: runtimeBundle.fileSearch },
|
|
9791
|
+
// Git metadata resolves against host storage, not a sandbox-internal cwd.
|
|
9792
|
+
git: { getWorkspaceRoot: () => getOptionalRuntimeBundleStorageRoot(runtimeBundle) }
|
|
9793
|
+
},
|
|
9794
|
+
chat: { service: agentRuntime.service },
|
|
9795
|
+
systemPrompt: { harness },
|
|
9796
|
+
skills: {
|
|
9797
|
+
workspaceRoot,
|
|
9798
|
+
additionalSkillPaths: runtimePi.additionalSkillPaths,
|
|
9799
|
+
piPackages: runtimePi.packages,
|
|
9800
|
+
noSkills: runtimePi.noSkills,
|
|
9801
|
+
getAdditionalSkillPaths: () => [
|
|
9802
|
+
...getRuntimeProvisioning()?.skillPaths ?? [],
|
|
9803
|
+
...opts.pi?.additionalSkillPaths ?? [],
|
|
9804
|
+
...opts.pi?.getHotReloadableResources?.().additionalSkillPaths ?? []
|
|
9805
|
+
],
|
|
9806
|
+
getPiPackages: () => [
|
|
9807
|
+
...opts.pi?.packages ?? [],
|
|
9808
|
+
...opts.pi?.getHotReloadableResources?.().packages ?? []
|
|
9809
|
+
]
|
|
9810
|
+
},
|
|
9811
|
+
catalog: { tools },
|
|
9812
|
+
commands: {
|
|
9813
|
+
harness,
|
|
9814
|
+
defaultSessionId: sessionId,
|
|
9815
|
+
workdir: runtimeBundle.workspace.root,
|
|
9816
|
+
metering: opts.metering
|
|
9817
|
+
},
|
|
9818
|
+
reload: {
|
|
9819
|
+
harness,
|
|
9820
|
+
defaultSessionId: sessionId,
|
|
9821
|
+
beforeReload: opts.beforeReload,
|
|
9822
|
+
onDiagnostics: (diagnostics) => {
|
|
9823
|
+
lastReloadDiagnostics = diagnostics;
|
|
9824
|
+
}
|
|
9825
|
+
},
|
|
9826
|
+
readyStatus: { tracker: readyTracker }
|
|
9827
|
+
};
|
|
9626
9828
|
}
|
|
9627
9829
|
|
|
9628
9830
|
// src/server/http/csp.ts
|
|
@@ -9650,6 +9852,7 @@ function applyCspHeaders(response, opts = {}) {
|
|
|
9650
9852
|
}
|
|
9651
9853
|
|
|
9652
9854
|
// src/server/registerAgentRoutes.ts
|
|
9855
|
+
import { createHash as createHash6 } from "crypto";
|
|
9653
9856
|
import { basename as basename2 } from "path";
|
|
9654
9857
|
import { AuthStorage as AuthStorage2, ModelRegistry as ModelRegistry2 } from "@mariozechner/pi-coding-agent";
|
|
9655
9858
|
|
|
@@ -9795,6 +9998,8 @@ function buildUploadAgentTools(bundle) {
|
|
|
9795
9998
|
// src/server/registerAgentRoutes.ts
|
|
9796
9999
|
var DEFAULT_VERSION2 = "0.1.0-dev";
|
|
9797
10000
|
var DEFAULT_WORKSPACE_ID4 = "default";
|
|
10001
|
+
var PURE_RUNTIME_MODE2 = "none";
|
|
10002
|
+
var MAX_PURE_BINDINGS = 256;
|
|
9798
10003
|
var STANDARD_AGENT_TOOL_NAMES = ["bash", "read", "write", "edit", "find", "grep", "ls"];
|
|
9799
10004
|
function pluginNameFromPath(path4) {
|
|
9800
10005
|
const fileName = basename2(path4);
|
|
@@ -9814,6 +10019,20 @@ function getAvailableModelProviders() {
|
|
|
9814
10019
|
new Set(availableModels.map((model) => model.provider))
|
|
9815
10020
|
).sort((a, b) => a.localeCompare(b));
|
|
9816
10021
|
}
|
|
10022
|
+
function registerAgentCapabilitiesContributor(app, profile) {
|
|
10023
|
+
const host = app;
|
|
10024
|
+
if (typeof host.registerCapabilitiesContributor !== "function") return;
|
|
10025
|
+
host.registerCapabilitiesContributor(
|
|
10026
|
+
"agent",
|
|
10027
|
+
() => ({
|
|
10028
|
+
agent: {
|
|
10029
|
+
runtimeMode: profile.runtimeMode,
|
|
10030
|
+
tools: profile.capabilities.tools,
|
|
10031
|
+
modelProviders: getAvailableModelProviders()
|
|
10032
|
+
}
|
|
10033
|
+
})
|
|
10034
|
+
);
|
|
10035
|
+
}
|
|
9817
10036
|
function getRequestWorkspaceId2(request) {
|
|
9818
10037
|
return request.workspaceContext?.workspaceId ?? DEFAULT_WORKSPACE_ID4;
|
|
9819
10038
|
}
|
|
@@ -9839,6 +10058,14 @@ function normalizeSessionNamespace(value) {
|
|
|
9839
10058
|
const trimmed = value.trim();
|
|
9840
10059
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
9841
10060
|
}
|
|
10061
|
+
function pureSessionNamespaceFromWorkspaceId(workspaceId) {
|
|
10062
|
+
const digest = createHash6("sha256").update(workspaceId).digest("hex").slice(0, 16);
|
|
10063
|
+
return `workspace-${digest}`;
|
|
10064
|
+
}
|
|
10065
|
+
function pureSessionNamespaceFromScope(parts) {
|
|
10066
|
+
const digest = createHash6("sha256").update(JSON.stringify(parts)).digest("hex").slice(0, 16);
|
|
10067
|
+
return `scope-${digest}`;
|
|
10068
|
+
}
|
|
9842
10069
|
function getRequestAuthSubject2(request) {
|
|
9843
10070
|
const userId = request?.user?.id;
|
|
9844
10071
|
if (typeof userId === "string" && userId.trim()) return userId.trim();
|
|
@@ -9895,16 +10122,20 @@ function createRuntimeReadinessCheck(workspaceId, getRuntimeDependencies) {
|
|
|
9895
10122
|
};
|
|
9896
10123
|
}
|
|
9897
10124
|
var registerAgentRoutes = async (app, opts) => {
|
|
9898
|
-
const workspaceRoot = opts.workspaceRoot ?? process.cwd();
|
|
9899
10125
|
const sessionId = opts.sessionId ?? DEFAULT_WORKSPACE_ID4;
|
|
9900
|
-
const templatePath = opts.templatePath ?? getEnv("BORING_AGENT_TEMPLATE_PATH");
|
|
9901
10126
|
const resolvedMode = opts.runtimeModeAdapter?.id ?? opts.mode ?? autoDetectMode();
|
|
10127
|
+
if (!opts.runtimeModeAdapter && resolvedMode === PURE_RUNTIME_MODE2) {
|
|
10128
|
+
await registerPureAgentRoutes(app, opts, sessionId, resolvedMode);
|
|
10129
|
+
return;
|
|
10130
|
+
}
|
|
10131
|
+
const workspaceRoot = opts.workspaceRoot ?? process.cwd();
|
|
10132
|
+
const templatePath = opts.templatePath ?? getEnv("BORING_AGENT_TEMPLATE_PATH");
|
|
9902
10133
|
const modeAdapter = opts.runtimeModeAdapter ?? resolveMode(resolvedMode, { sandboxHandleStore: opts.sandboxHandleStore });
|
|
9903
10134
|
app.addHook("onClose", async () => {
|
|
9904
10135
|
await modeAdapter.dispose?.();
|
|
9905
10136
|
});
|
|
9906
10137
|
const modelsWorkspaceScoped = Boolean(opts.filterModels);
|
|
9907
|
-
const requestScopedRuntime = typeof opts.getWorkspaceId === "function" || typeof opts.getWorkspaceRoot === "function" || typeof opts.getTemplatePath === "function" || typeof opts.getPi === "function" || typeof opts.getExtraTools === "function" || typeof opts.getSessionNamespace === "function" || typeof opts.getSystemPromptDynamic === "function";
|
|
10138
|
+
const requestScopedRuntime = typeof opts.getWorkspaceId === "function" || typeof opts.getWorkspaceRoot === "function" || typeof opts.getTemplatePath === "function" || typeof opts.getPi === "function" || typeof opts.getExtraTools === "function" || typeof opts.getSessionNamespace === "function" || typeof opts.getSystemPromptDynamic === "function" || typeof opts.getTrustedWorkspaceRoot === "function";
|
|
9908
10139
|
const sessionChangesTracker = new InMemorySessionChangesTracker();
|
|
9909
10140
|
const externalPluginsEnabled = opts.externalPlugins !== false;
|
|
9910
10141
|
const runtimeBindings = /* @__PURE__ */ new Map();
|
|
@@ -9919,12 +10150,23 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
9919
10150
|
async function resolveScopePi(workspaceId, root, request) {
|
|
9920
10151
|
return withPiHarnessDefaults(opts.getPi ? await opts.getPi({ workspaceId, workspaceRoot: root, request }) : opts.pi);
|
|
9921
10152
|
}
|
|
9922
|
-
async function resolveRuntimeScope(workspaceId, request) {
|
|
9923
|
-
|
|
10153
|
+
async function resolveRuntimeScope(workspaceId, request, trustedCtx) {
|
|
10154
|
+
let root = workspaceRoot;
|
|
10155
|
+
if (request && opts.getWorkspaceRoot) {
|
|
10156
|
+
root = await opts.getWorkspaceRoot(workspaceId, request);
|
|
10157
|
+
} else if (trustedCtx && opts.getTrustedWorkspaceRoot) {
|
|
10158
|
+
root = await opts.getTrustedWorkspaceRoot(trustedCtx);
|
|
10159
|
+
} else if (!request && opts.getWorkspaceRoot) {
|
|
10160
|
+
throw createWorkspaceAgentDispatcherError(
|
|
10161
|
+
ErrorCode.enum.WORKSPACE_UNINITIALIZED,
|
|
10162
|
+
"workspace root resolution requires trusted workspace context",
|
|
10163
|
+
400
|
|
10164
|
+
);
|
|
10165
|
+
}
|
|
9924
10166
|
const scopedTemplatePath = opts.getTemplatePath ? await opts.getTemplatePath({ workspaceId, workspaceRoot: root, request }) : templatePath;
|
|
9925
10167
|
const pi = await resolveScopePi(workspaceId, root, request);
|
|
9926
|
-
const sessionNamespace = normalizeSessionNamespace(opts.getSessionNamespace ? await opts.getSessionNamespace({ workspaceId, workspaceRoot: root, request }) : opts.sessionNamespace);
|
|
9927
|
-
const extraToolsAuthSubject = opts.getExtraTools ? getRequestAuthSubject2(request) : void 0;
|
|
10168
|
+
const sessionNamespace = normalizeSessionNamespace(opts.getSessionNamespace ? await opts.getSessionNamespace({ workspaceId, workspaceRoot: root, request, userId: trustedCtx?.userId }) : opts.sessionNamespace);
|
|
10169
|
+
const extraToolsAuthSubject = opts.getExtraTools ? trustedCtx?.userId ?? getRequestAuthSubject2(request) : void 0;
|
|
9928
10170
|
return {
|
|
9929
10171
|
root,
|
|
9930
10172
|
templatePath: scopedTemplatePath,
|
|
@@ -9984,7 +10226,7 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
9984
10226
|
request
|
|
9985
10227
|
});
|
|
9986
10228
|
}
|
|
9987
|
-
async function createRuntimeBinding(workspaceId, scope, request) {
|
|
10229
|
+
async function createRuntimeBinding(workspaceId, scope, request, trustedCtx) {
|
|
9988
10230
|
const root = scope.root;
|
|
9989
10231
|
const modeCtx = {
|
|
9990
10232
|
workspaceRoot: root,
|
|
@@ -10132,7 +10374,7 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
10132
10374
|
workspaceRoot: root,
|
|
10133
10375
|
runtimeMode: resolvedMode,
|
|
10134
10376
|
workspaceFsCapability: runtimeBundle.workspace.fsCapability,
|
|
10135
|
-
authSubject: getRequestAuthSubject2(request)
|
|
10377
|
+
authSubject: trustedCtx?.userId ?? getRequestAuthSubject2(request)
|
|
10136
10378
|
}) : [];
|
|
10137
10379
|
const tools = mergeTools({
|
|
10138
10380
|
standardTools,
|
|
@@ -10208,12 +10450,12 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
10208
10450
|
startRuntimeProvisioning(request);
|
|
10209
10451
|
return binding;
|
|
10210
10452
|
}
|
|
10211
|
-
function createRuntimeBindingEntry(workspaceId, scope, request) {
|
|
10453
|
+
function createRuntimeBindingEntry(workspaceId, scope, request, trustedCtx) {
|
|
10212
10454
|
const entry = {
|
|
10213
10455
|
state: "pending",
|
|
10214
10456
|
promise: Promise.resolve(null)
|
|
10215
10457
|
};
|
|
10216
|
-
entry.promise = createRuntimeBinding(workspaceId, scope, request).then(
|
|
10458
|
+
entry.promise = createRuntimeBinding(workspaceId, scope, request, trustedCtx).then(
|
|
10217
10459
|
(binding) => {
|
|
10218
10460
|
entry.state = "ready";
|
|
10219
10461
|
return binding;
|
|
@@ -10229,7 +10471,7 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
10229
10471
|
return entry;
|
|
10230
10472
|
}
|
|
10231
10473
|
async function getOrCreateRuntimeBinding(workspaceId, request, options = {}) {
|
|
10232
|
-
const scope = await resolveRuntimeScope(workspaceId, request);
|
|
10474
|
+
const scope = await resolveRuntimeScope(workspaceId, request, options.trustedCtx);
|
|
10233
10475
|
const existing = runtimeBindings.get(scope.key);
|
|
10234
10476
|
if (existing) {
|
|
10235
10477
|
if (options.failIfPending && existing.state === "pending") {
|
|
@@ -10247,7 +10489,7 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
10247
10489
|
);
|
|
10248
10490
|
}
|
|
10249
10491
|
}
|
|
10250
|
-
const created = createRuntimeBindingEntry(workspaceId, scope, request);
|
|
10492
|
+
const created = createRuntimeBindingEntry(workspaceId, scope, request, options.trustedCtx);
|
|
10251
10493
|
runtimeBindings.set(scope.key, created);
|
|
10252
10494
|
evictRuntimeBindings();
|
|
10253
10495
|
if (options.failIfPending) {
|
|
@@ -10303,6 +10545,24 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
10303
10545
|
const staticBinding = requestScopedRuntime ? null : await getOrCreateRuntimeBinding(sessionId);
|
|
10304
10546
|
const skillsScopeByRequest = /* @__PURE__ */ new WeakMap();
|
|
10305
10547
|
const earlySessionStores = /* @__PURE__ */ new Map();
|
|
10548
|
+
opts.onWorkspaceAgentDispatcher?.({
|
|
10549
|
+
async resolve(ctx, options) {
|
|
10550
|
+
const boundCtx = normalizeWorkspaceAgentDispatcherContext(ctx);
|
|
10551
|
+
assertWorkspaceAgentDispatcherRequestContext(boundCtx, options?.request);
|
|
10552
|
+
if (staticBinding) {
|
|
10553
|
+
if (boundCtx.workspaceId !== sessionId) {
|
|
10554
|
+
throw createWorkspaceAgentDispatcherError(
|
|
10555
|
+
ErrorCode.enum.UNAUTHORIZED,
|
|
10556
|
+
"workspace agent dispatcher context does not match bound workspace",
|
|
10557
|
+
401
|
|
10558
|
+
);
|
|
10559
|
+
}
|
|
10560
|
+
return createBoundWorkspaceAgentDispatcher(staticBinding.agent, boundCtx);
|
|
10561
|
+
}
|
|
10562
|
+
const binding = await getOrCreateRuntimeBinding(boundCtx.workspaceId, options?.request, { trustedCtx: boundCtx });
|
|
10563
|
+
return createBoundWorkspaceAgentDispatcher(binding.agent, boundCtx);
|
|
10564
|
+
}
|
|
10565
|
+
});
|
|
10306
10566
|
function getSkillsScopeForRequest(request) {
|
|
10307
10567
|
let promise = skillsScopeByRequest.get(request);
|
|
10308
10568
|
if (!promise) {
|
|
@@ -10517,6 +10777,173 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
10517
10777
|
staticBinding ? { tracker: staticBinding.readyTracker } : { getTracker: async (request) => (await getBindingForRequest(request)).readyTracker }
|
|
10518
10778
|
);
|
|
10519
10779
|
};
|
|
10780
|
+
async function registerPureAgentRoutes(app, opts, sessionId, resolvedMode) {
|
|
10781
|
+
const pureBindings = /* @__PURE__ */ new Map();
|
|
10782
|
+
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" || typeof opts.getTrustedWorkspaceRoot === "function";
|
|
10783
|
+
function evictPureBindings() {
|
|
10784
|
+
if (pureBindings.size <= MAX_PURE_BINDINGS) return;
|
|
10785
|
+
const keys = Array.from(pureBindings.keys());
|
|
10786
|
+
for (let i = 0; i < keys.length - MAX_PURE_BINDINGS; i += 1) {
|
|
10787
|
+
const key = keys[i];
|
|
10788
|
+
if (!key) continue;
|
|
10789
|
+
const binding = pureBindings.get(key);
|
|
10790
|
+
pureBindings.delete(key);
|
|
10791
|
+
binding?.then((resolved) => resolved.agent.dispose()).catch(() => {
|
|
10792
|
+
});
|
|
10793
|
+
}
|
|
10794
|
+
}
|
|
10795
|
+
async function createPureBinding(request, trustedCtx) {
|
|
10796
|
+
const workspaceId = trustedCtx?.workspaceId ?? (request ? getRequestWorkspaceId2(request) : sessionId);
|
|
10797
|
+
let workspaceRoot = opts.workspaceRoot ?? "";
|
|
10798
|
+
if (request && opts.getWorkspaceRoot) {
|
|
10799
|
+
workspaceRoot = await opts.getWorkspaceRoot(workspaceId, request);
|
|
10800
|
+
} else if (trustedCtx && opts.getTrustedWorkspaceRoot) {
|
|
10801
|
+
workspaceRoot = await opts.getTrustedWorkspaceRoot(trustedCtx);
|
|
10802
|
+
} else if (!request && opts.getWorkspaceRoot) {
|
|
10803
|
+
throw createWorkspaceAgentDispatcherError(
|
|
10804
|
+
ErrorCode.enum.WORKSPACE_UNINITIALIZED,
|
|
10805
|
+
"workspace root resolution requires trusted workspace context",
|
|
10806
|
+
400
|
|
10807
|
+
);
|
|
10808
|
+
}
|
|
10809
|
+
const pi = withPiHarnessDefaults(opts.getPi ? await opts.getPi({ workspaceId, workspaceRoot, request }) : opts.pi);
|
|
10810
|
+
const configuredSessionNamespace = normalizeSessionNamespace(opts.getSessionNamespace ? await opts.getSessionNamespace({ workspaceId, workspaceRoot, request, userId: trustedCtx?.userId }) : opts.sessionNamespace);
|
|
10811
|
+
const authSubject = opts.getExtraTools ? trustedCtx?.userId ?? getRequestAuthSubject2(request) : void 0;
|
|
10812
|
+
const pureScopeNamespace = opts.getWorkspaceId ? pureSessionNamespaceFromWorkspaceId(workspaceId) : requestScopedPure ? pureSessionNamespaceFromScope([resolvedMode, workspaceId, workspaceRoot, pi, authSubject ?? null]) : void 0;
|
|
10813
|
+
const sessionNamespace = configuredSessionNamespace && pureScopeNamespace ? `${configuredSessionNamespace}-${pureScopeNamespace}` : configuredSessionNamespace ?? pureScopeNamespace;
|
|
10814
|
+
const key = JSON.stringify([
|
|
10815
|
+
resolvedMode,
|
|
10816
|
+
workspaceId,
|
|
10817
|
+
workspaceRoot,
|
|
10818
|
+
pi,
|
|
10819
|
+
sessionNamespace ?? null,
|
|
10820
|
+
authSubject ?? null
|
|
10821
|
+
]);
|
|
10822
|
+
const existing = pureBindings.get(key);
|
|
10823
|
+
if (existing) return existing;
|
|
10824
|
+
const bindingPromise = (async () => {
|
|
10825
|
+
const scopedExtraTools = opts.getExtraTools ? await opts.getExtraTools({
|
|
10826
|
+
workspaceId,
|
|
10827
|
+
workspaceRoot,
|
|
10828
|
+
runtimeMode: resolvedMode,
|
|
10829
|
+
workspaceFsCapability: "none",
|
|
10830
|
+
authSubject
|
|
10831
|
+
}) : [];
|
|
10832
|
+
const tools = mergeTools({
|
|
10833
|
+
standardTools: [],
|
|
10834
|
+
extraTools: [...opts.extraTools ?? [], ...scopedExtraTools],
|
|
10835
|
+
logger: app.log
|
|
10836
|
+
});
|
|
10837
|
+
const baseHarnessFactory = opts.harnessFactory ?? ((input) => createPiCodingAgentHarness({
|
|
10838
|
+
...input,
|
|
10839
|
+
pi
|
|
10840
|
+
}));
|
|
10841
|
+
const systemPromptDynamic = opts.getSystemPromptDynamic ? () => opts.getSystemPromptDynamic?.({ workspaceId, workspaceRoot }) : opts.systemPromptDynamic;
|
|
10842
|
+
const harnessFactory = ((input) => baseHarnessFactory({
|
|
10843
|
+
...input,
|
|
10844
|
+
sessionNamespace,
|
|
10845
|
+
sessionRoot: opts.sessionRoot,
|
|
10846
|
+
sessionDir: input.sessionDir
|
|
10847
|
+
}));
|
|
10848
|
+
const coreAgent = createAgentRuntimeBridge({
|
|
10849
|
+
runtime: "none",
|
|
10850
|
+
tools,
|
|
10851
|
+
harnessFactory,
|
|
10852
|
+
systemPromptAppend: opts.systemPromptAppend,
|
|
10853
|
+
systemPromptDynamic,
|
|
10854
|
+
telemetry: opts.telemetry,
|
|
10855
|
+
metering: opts.metering,
|
|
10856
|
+
sessionStorageRoot: opts.sessionRoot
|
|
10857
|
+
});
|
|
10858
|
+
const agentRuntime = await coreAgent.getRuntime();
|
|
10859
|
+
return {
|
|
10860
|
+
agent: coreAgent.agent,
|
|
10861
|
+
tools,
|
|
10862
|
+
readyTracker: new ReadyStatusTracker({ sandboxReady: true, harnessReady: true }),
|
|
10863
|
+
piChatService: agentRuntime.service
|
|
10864
|
+
};
|
|
10865
|
+
})();
|
|
10866
|
+
pureBindings.set(key, bindingPromise);
|
|
10867
|
+
bindingPromise.catch(() => {
|
|
10868
|
+
if (pureBindings.get(key) === bindingPromise) pureBindings.delete(key);
|
|
10869
|
+
});
|
|
10870
|
+
evictPureBindings();
|
|
10871
|
+
return bindingPromise;
|
|
10872
|
+
}
|
|
10873
|
+
const staticBinding = requestScopedPure ? void 0 : await createPureBinding();
|
|
10874
|
+
opts.onWorkspaceAgentDispatcher?.({
|
|
10875
|
+
async resolve(ctx, options) {
|
|
10876
|
+
const boundCtx = normalizeWorkspaceAgentDispatcherContext(ctx);
|
|
10877
|
+
assertWorkspaceAgentDispatcherRequestContext(boundCtx, options?.request);
|
|
10878
|
+
if (staticBinding) {
|
|
10879
|
+
if (boundCtx.workspaceId !== sessionId) {
|
|
10880
|
+
throw createWorkspaceAgentDispatcherError(
|
|
10881
|
+
ErrorCode.enum.UNAUTHORIZED,
|
|
10882
|
+
"workspace agent dispatcher context does not match bound workspace",
|
|
10883
|
+
401
|
|
10884
|
+
);
|
|
10885
|
+
}
|
|
10886
|
+
return createBoundWorkspaceAgentDispatcher(staticBinding.agent, boundCtx);
|
|
10887
|
+
}
|
|
10888
|
+
const binding = await createPureBinding(options?.request, boundCtx);
|
|
10889
|
+
return createBoundWorkspaceAgentDispatcher(binding.agent, boundCtx);
|
|
10890
|
+
}
|
|
10891
|
+
});
|
|
10892
|
+
const profile = {
|
|
10893
|
+
runtimeMode: resolvedMode,
|
|
10894
|
+
capabilities: {
|
|
10895
|
+
tools: staticBinding ? toolNames(staticBinding.tools) : toolNames(opts.extraTools ?? [])
|
|
10896
|
+
},
|
|
10897
|
+
sessionChangesTracker: new InMemorySessionChangesTracker(),
|
|
10898
|
+
health: {
|
|
10899
|
+
register: opts.registerHealthRoute ?? true,
|
|
10900
|
+
version: opts.version ?? DEFAULT_VERSION2,
|
|
10901
|
+
getReadiness: () => ({ sandboxReady: true, harnessReady: true })
|
|
10902
|
+
},
|
|
10903
|
+
chat: {
|
|
10904
|
+
...staticBinding ? { service: staticBinding.piChatService } : { getService: async (request) => (await createPureBinding(request)).piChatService },
|
|
10905
|
+
defaultWorkspaceId: false
|
|
10906
|
+
},
|
|
10907
|
+
models: { filterModels: opts.filterModels },
|
|
10908
|
+
catalog: staticBinding ? { tools: staticBinding.tools } : { getTools: async (request) => (await createPureBinding(request)).tools },
|
|
10909
|
+
readyStatus: staticBinding ? { tracker: staticBinding.readyTracker } : { getTracker: async (request) => (await createPureBinding(request)).readyTracker },
|
|
10910
|
+
dispose: async () => {
|
|
10911
|
+
const bindings = await Promise.allSettled(pureBindings.values());
|
|
10912
|
+
await Promise.all(bindings.filter((result) => result.status === "fulfilled").map((result) => result.value.agent.dispose()));
|
|
10913
|
+
},
|
|
10914
|
+
beforeRegister: (profileApp) => {
|
|
10915
|
+
profileApp.addHook("onRequest", async (request, reply) => {
|
|
10916
|
+
const user = request.user;
|
|
10917
|
+
let workspaceId = DEFAULT_WORKSPACE_ID4;
|
|
10918
|
+
if (opts.getWorkspaceId && !isWorkspaceAgnosticAgentRequest(request, {
|
|
10919
|
+
readyStatusWorkspaceScoped: requestScopedPure,
|
|
10920
|
+
modelsWorkspaceScoped: Boolean(opts.filterModels)
|
|
10921
|
+
})) {
|
|
10922
|
+
try {
|
|
10923
|
+
workspaceId = (await opts.getWorkspaceId(request)).trim();
|
|
10924
|
+
} catch (error) {
|
|
10925
|
+
const statusCode = typeof error?.statusCode === "number" ? error.statusCode : 400;
|
|
10926
|
+
const message = statusCode >= 500 ? "workspace scope failed" : error instanceof Error ? error.message : "workspace id is required";
|
|
10927
|
+
return reply.code(statusCode).send({
|
|
10928
|
+
error: { code: ErrorCode.enum.WORKSPACE_UNINITIALIZED, message }
|
|
10929
|
+
});
|
|
10930
|
+
}
|
|
10931
|
+
if (workspaceId.length === 0) {
|
|
10932
|
+
return reply.code(400).send({
|
|
10933
|
+
error: {
|
|
10934
|
+
code: ErrorCode.enum.WORKSPACE_UNINITIALIZED,
|
|
10935
|
+
message: "workspace id is required"
|
|
10936
|
+
}
|
|
10937
|
+
});
|
|
10938
|
+
}
|
|
10939
|
+
}
|
|
10940
|
+
request.workspaceContext = { workspaceId, authenticated: !!user };
|
|
10941
|
+
});
|
|
10942
|
+
}
|
|
10943
|
+
};
|
|
10944
|
+
registerAgentCapabilitiesContributor(app, profile);
|
|
10945
|
+
await registerAgentRouteBindingProfile(app, profile);
|
|
10946
|
+
}
|
|
10520
10947
|
export {
|
|
10521
10948
|
FileHandleStore,
|
|
10522
10949
|
MANAGED_AGENT_MCP_DELIVERY_RULE,
|