@skydiveai/pi-extensions 0.1.252-beta.1 → 0.1.252-beta.10
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/index.d.mts +19 -1
- package/dist/index.mjs +100 -24
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { A2AOptions, Middleware, ProtocolsOptions } from "@skydiveai/pi-server";
|
|
2
3
|
import { ExtensionFactory } from "@earendil-works/pi-coding-agent";
|
|
3
4
|
import { Type } from "typebox";
|
|
4
5
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
|
+
import * as _$qs from "qs";
|
|
7
|
+
import * as _$express from "express";
|
|
5
8
|
import { Logger } from "pino";
|
|
6
9
|
|
|
10
|
+
//#region ../../node_modules/@types/express-serve-static-core/index.d.ts
|
|
11
|
+
declare global {
|
|
12
|
+
namespace Express {
|
|
13
|
+
// These open interfaces may be extended in an application-specific manner via declaration merging.
|
|
14
|
+
// See for example method-override.d.ts (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/method-override/index.d.ts)
|
|
15
|
+
interface Request {}
|
|
16
|
+
interface Response {}
|
|
17
|
+
interface Locals {}
|
|
18
|
+
interface Application {}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
interface ParamsDictionary {
|
|
22
|
+
[key: string]: string;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
7
25
|
//#region src/harness.d.ts
|
|
8
26
|
type CreateHarnessOptions = ProtocolsOptions & {
|
|
9
27
|
cwd: string;
|
|
@@ -26,7 +44,7 @@ declare function createHarness(options: CreateHarnessOptions): {
|
|
|
26
44
|
};
|
|
27
45
|
protocols: {
|
|
28
46
|
handlers: {
|
|
29
|
-
/** Express-style; mount at /a2a. */a2a: RequestHandler
|
|
47
|
+
/** Express-style; mount at /a2a. */a2a: _$express.RequestHandler<ParamsDictionary, any, any, _$qs.ParsedQs, Record<string, any>>; /** GET /.well-known/agent-card.json. */
|
|
30
48
|
agentCard: Middleware; /** Mirrors each vendor's API shape. */
|
|
31
49
|
openai: {
|
|
32
50
|
v1: {
|
package/dist/index.mjs
CHANGED
|
@@ -161,7 +161,7 @@ async function readPackageVersion(pkg) {
|
|
|
161
161
|
let dir;
|
|
162
162
|
try {
|
|
163
163
|
dir = dirname(require.resolve(pkg));
|
|
164
|
-
} catch {
|
|
164
|
+
} catch (_error) {
|
|
165
165
|
return null;
|
|
166
166
|
}
|
|
167
167
|
let current = dir;
|
|
@@ -169,7 +169,7 @@ async function readPackageVersion(pkg) {
|
|
|
169
169
|
try {
|
|
170
170
|
const manifest = JSON.parse(await readFile(resolve(current, "package.json"), "utf8"));
|
|
171
171
|
if (manifest.name === pkg && typeof manifest.version === "string") return manifest.version;
|
|
172
|
-
} catch {}
|
|
172
|
+
} catch (_error) {}
|
|
173
173
|
const parent = dirname(current);
|
|
174
174
|
if (parent === current) return null;
|
|
175
175
|
current = parent;
|
|
@@ -210,7 +210,7 @@ function createPlatformEnvMiddleware() {
|
|
|
210
210
|
return async (req, _res, next) => {
|
|
211
211
|
try {
|
|
212
212
|
await loadPlatformConfig(new Request(requestUrl(req), { headers: requestHeaders(req) }));
|
|
213
|
-
} catch {}
|
|
213
|
+
} catch (_error) {}
|
|
214
214
|
next();
|
|
215
215
|
};
|
|
216
216
|
}
|
|
@@ -662,7 +662,7 @@ async function listToolFiles(dir) {
|
|
|
662
662
|
file,
|
|
663
663
|
mtimeMs: s.mtimeMs
|
|
664
664
|
});
|
|
665
|
-
} catch {}
|
|
665
|
+
} catch (_error) {}
|
|
666
666
|
}
|
|
667
667
|
return out;
|
|
668
668
|
}
|
|
@@ -946,7 +946,7 @@ function probeAlive(pid) {
|
|
|
946
946
|
try {
|
|
947
947
|
process.kill(pid, 0);
|
|
948
948
|
return true;
|
|
949
|
-
} catch {
|
|
949
|
+
} catch (_error) {
|
|
950
950
|
return false;
|
|
951
951
|
}
|
|
952
952
|
}
|
|
@@ -1041,7 +1041,7 @@ async function connectClient(id, config, opts = {}) {
|
|
|
1041
1041
|
}
|
|
1042
1042
|
try {
|
|
1043
1043
|
await client.close();
|
|
1044
|
-
} catch {}
|
|
1044
|
+
} catch (_error) {}
|
|
1045
1045
|
return {
|
|
1046
1046
|
status: "failed",
|
|
1047
1047
|
error: formatError(result),
|
|
@@ -1163,7 +1163,7 @@ function estimateToolTokens(tool) {
|
|
|
1163
1163
|
let chars = tool.name.length + (tool.description?.length ?? 0);
|
|
1164
1164
|
if (tool.inputSchema !== void 0 && tool.inputSchema !== null) try {
|
|
1165
1165
|
chars += JSON.stringify(tool.inputSchema).length;
|
|
1166
|
-
} catch {
|
|
1166
|
+
} catch (_error) {
|
|
1167
1167
|
chars += 256;
|
|
1168
1168
|
}
|
|
1169
1169
|
return Math.ceil(chars / 4);
|
|
@@ -2163,7 +2163,7 @@ function extractMessageId(channelContextJson) {
|
|
|
2163
2163
|
try {
|
|
2164
2164
|
const parsed = channelContextRefSchema.safeParse(JSON.parse(channelContextJson));
|
|
2165
2165
|
return parsed.success ? parsed.data.messageId : null;
|
|
2166
|
-
} catch {
|
|
2166
|
+
} catch (_error) {
|
|
2167
2167
|
return null;
|
|
2168
2168
|
}
|
|
2169
2169
|
}
|
|
@@ -2410,7 +2410,7 @@ async function postSubagentSpawn({ messageId, tasks }) {
|
|
|
2410
2410
|
try {
|
|
2411
2411
|
const errBody = await res.json();
|
|
2412
2412
|
if (errBody && typeof errBody.error === "string") detail = `: ${errBody.error}`;
|
|
2413
|
-
} catch {}
|
|
2413
|
+
} catch (_error) {}
|
|
2414
2414
|
throw new Error(`subagent-spawn POST failed (${res.status})${detail}`);
|
|
2415
2415
|
}
|
|
2416
2416
|
const body = await res.json();
|
|
@@ -2434,7 +2434,7 @@ async function postSubagentRearm({ messageId, taskId, timeoutMinutes }) {
|
|
|
2434
2434
|
try {
|
|
2435
2435
|
const body = await res.json();
|
|
2436
2436
|
if (typeof body.error === "string") detail = `: ${body.error}`;
|
|
2437
|
-
} catch {}
|
|
2437
|
+
} catch (_error) {}
|
|
2438
2438
|
throw new Error(`subagent re-arm POST failed (${res.status})${detail}`);
|
|
2439
2439
|
}
|
|
2440
2440
|
const body = await res.json();
|
|
@@ -3028,6 +3028,13 @@ const bashDefaultTimeoutExtension = (pi) => {
|
|
|
3028
3028
|
* idle sandbox's resource usage is not the agent's problem and there is no
|
|
3029
3029
|
* open turn to deliver into anyway.
|
|
3030
3030
|
*
|
|
3031
|
+
* Delivery is further gated to turns that are ACTIVELY in tool-call work
|
|
3032
|
+
* (see WarningDeliveryGate): a warning that lands once the model has moved
|
|
3033
|
+
* on to its final reply triggers a continuation turn after the answer was
|
|
3034
|
+
* already produced, which breaks external-channel delivery (Slack steer-
|
|
3035
|
+
* after-reply). So the warning only interrupts between/around tool calls
|
|
3036
|
+
* and never chases a turn that is already wrapping up.
|
|
3037
|
+
*
|
|
3031
3038
|
* Best-effort throughout: any read failure (cgroup absent, controller not
|
|
3032
3039
|
* delegated, non-cgroup-v2 host, df missing) reads as "no signal" for that
|
|
3033
3040
|
* stat and the extension warns on what it can see — it must never break a
|
|
@@ -3151,20 +3158,77 @@ function resourcePressureWarningText(trigger, { mem, cpuPct, diskPct }) {
|
|
|
3151
3158
|
const closer = trigger === "memory" ? "If the workload genuinely needs more memory or CPU, request a bigger sandbox with `platform compute request`." : "A bigger compute tier will not add disk. Free space on the current computer instead, and do not request a compute upgrade for disk pressure.";
|
|
3152
3159
|
return `<system_notification>${lead} Current usage: ${stats.join(", ")}. Act now: ${remedy} ${closer} Do not mention this constraint as you work unless the user asks. This warning does not change whether the user is owed a reply. Do not call \`platform channel suppress-reply\` because of this warning. Complete any reply already owed after checkpointing. This is an automated resource warning, not a message from the user; continue the task, adjusted.</system_notification>`;
|
|
3153
3160
|
}
|
|
3161
|
+
/**
|
|
3162
|
+
* Whether a pressure warning may be delivered into the open turn right now.
|
|
3163
|
+
*
|
|
3164
|
+
* Only interrupts turns that are actively in tool-call work. The failure it
|
|
3165
|
+
* guards against: the poll lands after the model has stopped calling tools
|
|
3166
|
+
* and is streaming (or has finished) its final reply. `sendMessage` with
|
|
3167
|
+
* `triggerTurn: true` then starts a continuation turn BEHIND an answer the
|
|
3168
|
+
* user is already owed, which wedges external-channel delivery (Slack).
|
|
3169
|
+
*
|
|
3170
|
+
* Deliverable means ALL of:
|
|
3171
|
+
* - agent active (between agent_start and agent_end),
|
|
3172
|
+
* - the turn has issued at least one tool call,
|
|
3173
|
+
* - no assistant message is currently streaming (while one streams we cannot
|
|
3174
|
+
* know whether it will end the loop, so we hold and re-poll),
|
|
3175
|
+
* - the loop is not closed: no assistant message without tool calls has
|
|
3176
|
+
* completed this turn (that message IS the final reply; anything after it
|
|
3177
|
+
* is post-answer and must not be steered).
|
|
3178
|
+
*
|
|
3179
|
+
* Pure and exported so the gating is regression-testable without fake timers.
|
|
3180
|
+
*/
|
|
3181
|
+
var WarningDeliveryGate = class {
|
|
3182
|
+
agentActive = false;
|
|
3183
|
+
sawToolCall = false;
|
|
3184
|
+
toolsInFlight = 0;
|
|
3185
|
+
messageStreaming = false;
|
|
3186
|
+
loopClosed = false;
|
|
3187
|
+
agentStart() {
|
|
3188
|
+
this.agentActive = true;
|
|
3189
|
+
this.sawToolCall = false;
|
|
3190
|
+
this.toolsInFlight = 0;
|
|
3191
|
+
this.messageStreaming = false;
|
|
3192
|
+
this.loopClosed = false;
|
|
3193
|
+
}
|
|
3194
|
+
agentEnd() {
|
|
3195
|
+
this.agentActive = false;
|
|
3196
|
+
this.messageStreaming = false;
|
|
3197
|
+
}
|
|
3198
|
+
toolExecutionStart() {
|
|
3199
|
+
this.sawToolCall = true;
|
|
3200
|
+
this.toolsInFlight += 1;
|
|
3201
|
+
}
|
|
3202
|
+
toolExecutionEnd() {
|
|
3203
|
+
this.toolsInFlight = Math.max(0, this.toolsInFlight - 1);
|
|
3204
|
+
}
|
|
3205
|
+
messageStart(message) {
|
|
3206
|
+
if (message.role !== "assistant") return;
|
|
3207
|
+
this.messageStreaming = true;
|
|
3208
|
+
}
|
|
3209
|
+
messageEnd(message) {
|
|
3210
|
+
if (message.role !== "assistant") return;
|
|
3211
|
+
this.messageStreaming = false;
|
|
3212
|
+
if (!(Array.isArray(message.content) && message.content.some((part) => part.type === "toolCall"))) this.loopClosed = true;
|
|
3213
|
+
}
|
|
3214
|
+
canDeliver() {
|
|
3215
|
+
return this.agentActive && this.sawToolCall && !this.loopClosed && !this.messageStreaming;
|
|
3216
|
+
}
|
|
3217
|
+
};
|
|
3154
3218
|
const resourcePressureWarningExtension = (pi) => {
|
|
3155
|
-
|
|
3219
|
+
const gate = new WarningDeliveryGate();
|
|
3156
3220
|
let warnedMemThisTurn = false;
|
|
3157
3221
|
let warnedDiskThisTurn = false;
|
|
3158
3222
|
let timer = null;
|
|
3159
3223
|
const sampleCpu = createCpuSampler();
|
|
3160
3224
|
async function checkOnce() {
|
|
3161
|
-
if (!
|
|
3225
|
+
if (!gate.canDeliver() || warnedMemThisTurn && warnedDiskThisTurn) return;
|
|
3162
3226
|
const [mem, cpuPct, diskPct] = await Promise.all([
|
|
3163
3227
|
readMemUsePct(),
|
|
3164
3228
|
sampleCpu(),
|
|
3165
3229
|
readDiskUsePct()
|
|
3166
3230
|
]);
|
|
3167
|
-
if (!
|
|
3231
|
+
if (!gate.canDeliver()) return;
|
|
3168
3232
|
let trigger = null;
|
|
3169
3233
|
if (!warnedMemThisTurn && mem !== null && mem.pct >= 80) {
|
|
3170
3234
|
trigger = "memory";
|
|
@@ -3193,8 +3257,20 @@ const resourcePressureWarningExtension = (pi) => {
|
|
|
3193
3257
|
deliverAs: "followUp"
|
|
3194
3258
|
});
|
|
3195
3259
|
}
|
|
3260
|
+
pi.on("tool_execution_start", () => {
|
|
3261
|
+
gate.toolExecutionStart();
|
|
3262
|
+
});
|
|
3263
|
+
pi.on("tool_execution_end", () => {
|
|
3264
|
+
gate.toolExecutionEnd();
|
|
3265
|
+
});
|
|
3266
|
+
pi.on("message_start", (event) => {
|
|
3267
|
+
gate.messageStart(event.message);
|
|
3268
|
+
});
|
|
3269
|
+
pi.on("message_end", (event) => {
|
|
3270
|
+
gate.messageEnd(event.message);
|
|
3271
|
+
});
|
|
3196
3272
|
pi.on("agent_start", async () => {
|
|
3197
|
-
|
|
3273
|
+
gate.agentStart();
|
|
3198
3274
|
warnedMemThisTurn = false;
|
|
3199
3275
|
warnedDiskThisTurn = false;
|
|
3200
3276
|
if (!timer) {
|
|
@@ -3207,7 +3283,7 @@ const resourcePressureWarningExtension = (pi) => {
|
|
|
3207
3283
|
}
|
|
3208
3284
|
});
|
|
3209
3285
|
pi.on("agent_end", async () => {
|
|
3210
|
-
|
|
3286
|
+
gate.agentEnd();
|
|
3211
3287
|
if (timer) {
|
|
3212
3288
|
clearInterval(timer);
|
|
3213
3289
|
timer = null;
|
|
@@ -3708,7 +3784,7 @@ async function readMemoryIndexFile({ cwd }) {
|
|
|
3708
3784
|
try {
|
|
3709
3785
|
const trimmed = (await readFile(path, "utf-8")).trim();
|
|
3710
3786
|
return trimmed.length > 0 ? trimmed : null;
|
|
3711
|
-
} catch {
|
|
3787
|
+
} catch (_error) {
|
|
3712
3788
|
return null;
|
|
3713
3789
|
}
|
|
3714
3790
|
}
|
|
@@ -3748,7 +3824,7 @@ async function maxMtimeAcrossDir(dir) {
|
|
|
3748
3824
|
let dirStat;
|
|
3749
3825
|
try {
|
|
3750
3826
|
dirStat = await stat(dir);
|
|
3751
|
-
} catch {
|
|
3827
|
+
} catch (_error) {
|
|
3752
3828
|
return null;
|
|
3753
3829
|
}
|
|
3754
3830
|
if (!dirStat.isDirectory()) return null;
|
|
@@ -3763,7 +3839,7 @@ async function walkMdFiles(dir, out) {
|
|
|
3763
3839
|
let entries;
|
|
3764
3840
|
try {
|
|
3765
3841
|
entries = await readdir(dir, { withFileTypes: true });
|
|
3766
|
-
} catch {
|
|
3842
|
+
} catch (_error) {
|
|
3767
3843
|
return;
|
|
3768
3844
|
}
|
|
3769
3845
|
await Promise.all(entries.map(async (entry) => {
|
|
@@ -3776,7 +3852,7 @@ async function listMdFilesShallow(dir) {
|
|
|
3776
3852
|
let entries;
|
|
3777
3853
|
try {
|
|
3778
3854
|
entries = await readdir(dir, { withFileTypes: true });
|
|
3779
|
-
} catch {
|
|
3855
|
+
} catch (_error) {
|
|
3780
3856
|
return [];
|
|
3781
3857
|
}
|
|
3782
3858
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => join(dir, e.name));
|
|
@@ -3785,7 +3861,7 @@ async function listSubdirs(dir) {
|
|
|
3785
3861
|
let entries;
|
|
3786
3862
|
try {
|
|
3787
3863
|
entries = await readdir(dir, { withFileTypes: true });
|
|
3788
|
-
} catch {
|
|
3864
|
+
} catch (_error) {
|
|
3789
3865
|
return [];
|
|
3790
3866
|
}
|
|
3791
3867
|
return entries.filter((e) => e.isDirectory()).map((e) => join(dir, e.name));
|
|
@@ -3811,14 +3887,14 @@ async function readFrontmatterOnly(filePath) {
|
|
|
3811
3887
|
let fh;
|
|
3812
3888
|
try {
|
|
3813
3889
|
fh = await open(filePath, "r");
|
|
3814
|
-
} catch {
|
|
3890
|
+
} catch (_error) {
|
|
3815
3891
|
return null;
|
|
3816
3892
|
}
|
|
3817
3893
|
try {
|
|
3818
3894
|
const buf = Buffer.alloc(FRONTMATTER_READ_BYTES);
|
|
3819
3895
|
const { bytesRead } = await fh.read(buf, 0, FRONTMATTER_READ_BYTES, 0);
|
|
3820
3896
|
return parseFrontmatter(buf.toString("utf-8", 0, bytesRead));
|
|
3821
|
-
} catch {
|
|
3897
|
+
} catch (_error) {
|
|
3822
3898
|
return null;
|
|
3823
3899
|
} finally {
|
|
3824
3900
|
await fh.close().catch(() => {});
|
|
@@ -3830,7 +3906,7 @@ function parseFrontmatter(text) {
|
|
|
3830
3906
|
let parsed;
|
|
3831
3907
|
try {
|
|
3832
3908
|
parsed = parse(match[1]);
|
|
3833
|
-
} catch {
|
|
3909
|
+
} catch (_error) {
|
|
3834
3910
|
return null;
|
|
3835
3911
|
}
|
|
3836
3912
|
const result = FrontmatterSchema.safeParse(parsed);
|
|
@@ -4826,7 +4902,7 @@ async function readLogChunk(id, maxBytes, anchor) {
|
|
|
4826
4902
|
text: buffer.toString("utf8", start),
|
|
4827
4903
|
size
|
|
4828
4904
|
};
|
|
4829
|
-
} catch {
|
|
4905
|
+
} catch (_error) {
|
|
4830
4906
|
return {
|
|
4831
4907
|
text: "",
|
|
4832
4908
|
size: 0
|