@openape/ape-agent 2.9.0 → 2.9.2
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/bridge.mjs +80 -52
- package/package.json +4 -4
package/dist/bridge.mjs
CHANGED
|
@@ -1541,7 +1541,62 @@ import { basename, join as join22 } from "path";
|
|
|
1541
1541
|
|
|
1542
1542
|
// ../../packages/core/dist/index.js
|
|
1543
1543
|
import * as jose from "jose";
|
|
1544
|
+
import { lookup } from "dns/promises";
|
|
1545
|
+
import { isIP } from "net";
|
|
1544
1546
|
var HKDF_INFO = new TextEncoder().encode("openape-sealed-box-v1");
|
|
1547
|
+
function isBlockedAddress(ip) {
|
|
1548
|
+
const fam = isIP(ip);
|
|
1549
|
+
if (fam === 4) {
|
|
1550
|
+
const o3 = ip.split(".");
|
|
1551
|
+
const a2 = Number(o3[0]);
|
|
1552
|
+
const b2 = Number(o3[1]);
|
|
1553
|
+
if (a2 === 0) return true;
|
|
1554
|
+
if (a2 === 127) return true;
|
|
1555
|
+
if (a2 === 10) return true;
|
|
1556
|
+
if (a2 === 172 && b2 >= 16 && b2 <= 31) return true;
|
|
1557
|
+
if (a2 === 192 && b2 === 168) return true;
|
|
1558
|
+
if (a2 === 169 && b2 === 254) return true;
|
|
1559
|
+
if (a2 === 100 && b2 >= 64 && b2 <= 127) return true;
|
|
1560
|
+
return false;
|
|
1561
|
+
}
|
|
1562
|
+
const low = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
1563
|
+
if (low === "::" || low === "::1") return true;
|
|
1564
|
+
if (low.startsWith("fe80")) return true;
|
|
1565
|
+
if (low.startsWith("fc") || low.startsWith("fd")) return true;
|
|
1566
|
+
const mapped = low.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/);
|
|
1567
|
+
if (mapped) return isBlockedAddress(mapped[1]);
|
|
1568
|
+
return false;
|
|
1569
|
+
}
|
|
1570
|
+
async function assertPublicUrl(rawUrl, opts = {}) {
|
|
1571
|
+
let url;
|
|
1572
|
+
try {
|
|
1573
|
+
url = new URL(rawUrl);
|
|
1574
|
+
} catch {
|
|
1575
|
+
throw new Error(`Invalid URL: ${rawUrl}`);
|
|
1576
|
+
}
|
|
1577
|
+
const allowedSchemes = opts.allowHttp === true ? /* @__PURE__ */ new Set(["https:", "http:"]) : /* @__PURE__ */ new Set(["https:"]);
|
|
1578
|
+
if (!allowedSchemes.has(url.protocol)) {
|
|
1579
|
+
const expected = opts.allowHttp === true ? "http(s)" : "https";
|
|
1580
|
+
throw new Error(`URL must use ${expected}:// (got ${url.protocol}//) \u2014 ${rawUrl}`);
|
|
1581
|
+
}
|
|
1582
|
+
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
1583
|
+
const addresses = [];
|
|
1584
|
+
if (isIP(host)) {
|
|
1585
|
+
addresses.push(host);
|
|
1586
|
+
} else {
|
|
1587
|
+
const results = await lookup(host, { all: true });
|
|
1588
|
+
for (const r3 of results) addresses.push(r3.address);
|
|
1589
|
+
}
|
|
1590
|
+
if (addresses.length === 0) {
|
|
1591
|
+
throw new Error(`Host did not resolve: ${host}`);
|
|
1592
|
+
}
|
|
1593
|
+
for (const addr of addresses) {
|
|
1594
|
+
if (isBlockedAddress(addr)) {
|
|
1595
|
+
throw new Error(`Refusing to fetch a private/loopback address (${addr}) for ${rawUrl}`);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return url;
|
|
1599
|
+
}
|
|
1545
1600
|
|
|
1546
1601
|
// ../../packages/grants/dist/index.js
|
|
1547
1602
|
function normalizeSelector(selector) {
|
|
@@ -3090,8 +3145,6 @@ import { dirname, normalize, resolve } from "path";
|
|
|
3090
3145
|
import { homedir as homedir23 } from "os";
|
|
3091
3146
|
import { resolve as resolve2 } from "path";
|
|
3092
3147
|
import process2 from "process";
|
|
3093
|
-
import { lookup } from "dns/promises";
|
|
3094
|
-
import { isIP } from "net";
|
|
3095
3148
|
import { execFileSync } from "child_process";
|
|
3096
3149
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
3097
3150
|
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
@@ -3584,59 +3637,10 @@ var gitWorktreeTools = [
|
|
|
3584
3637
|
}
|
|
3585
3638
|
}
|
|
3586
3639
|
];
|
|
3587
|
-
function isBlockedAddress(ip) {
|
|
3588
|
-
const fam = isIP(ip);
|
|
3589
|
-
if (fam === 4) {
|
|
3590
|
-
const o3 = ip.split(".");
|
|
3591
|
-
const a2 = Number(o3[0]);
|
|
3592
|
-
const b2 = Number(o3[1]);
|
|
3593
|
-
if (a2 === 0) return true;
|
|
3594
|
-
if (a2 === 127) return true;
|
|
3595
|
-
if (a2 === 10) return true;
|
|
3596
|
-
if (a2 === 172 && b2 >= 16 && b2 <= 31) return true;
|
|
3597
|
-
if (a2 === 192 && b2 === 168) return true;
|
|
3598
|
-
if (a2 === 169 && b2 === 254) return true;
|
|
3599
|
-
if (a2 === 100 && b2 >= 64 && b2 <= 127) return true;
|
|
3600
|
-
return false;
|
|
3601
|
-
}
|
|
3602
|
-
const low = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
3603
|
-
if (low === "::" || low === "::1") return true;
|
|
3604
|
-
if (low.startsWith("fe80")) return true;
|
|
3605
|
-
if (low.startsWith("fc") || low.startsWith("fd")) return true;
|
|
3606
|
-
const mapped = low.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/);
|
|
3607
|
-
if (mapped) return isBlockedAddress(mapped[1]);
|
|
3608
|
-
return false;
|
|
3609
|
-
}
|
|
3610
|
-
async function assertPublicUrl(rawUrl) {
|
|
3611
|
-
let url;
|
|
3612
|
-
try {
|
|
3613
|
-
url = new URL(rawUrl);
|
|
3614
|
-
} catch {
|
|
3615
|
-
throw new Error(`Invalid URL: ${rawUrl}`);
|
|
3616
|
-
}
|
|
3617
|
-
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
3618
|
-
throw new Error(`url must be http(s) (got ${url.protocol})`);
|
|
3619
|
-
}
|
|
3620
|
-
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
3621
|
-
const addresses = [];
|
|
3622
|
-
if (isIP(host)) {
|
|
3623
|
-
addresses.push(host);
|
|
3624
|
-
} else {
|
|
3625
|
-
const results = await lookup(host, { all: true });
|
|
3626
|
-
for (const r3 of results) addresses.push(r3.address);
|
|
3627
|
-
}
|
|
3628
|
-
if (addresses.length === 0) throw new Error(`Host did not resolve: ${host}`);
|
|
3629
|
-
for (const addr of addresses) {
|
|
3630
|
-
if (isBlockedAddress(addr)) {
|
|
3631
|
-
throw new Error(`Refusing to fetch a private/loopback address (${addr}) for ${rawUrl}`);
|
|
3632
|
-
}
|
|
3633
|
-
}
|
|
3634
|
-
return url;
|
|
3635
|
-
}
|
|
3636
3640
|
async function safeFetch(rawUrl, init2 = {}, maxRedirects = 5) {
|
|
3637
3641
|
let current = rawUrl;
|
|
3638
3642
|
for (let hop = 0; hop <= maxRedirects; hop += 1) {
|
|
3639
|
-
await assertPublicUrl(current);
|
|
3643
|
+
await assertPublicUrl(current, { allowHttp: true });
|
|
3640
3644
|
const res = await fetch(current, { ...init2, redirect: "manual" });
|
|
3641
3645
|
if (res.status >= 300 && res.status < 400) {
|
|
3642
3646
|
const location = res.headers.get("location");
|
|
@@ -4707,6 +4711,8 @@ function createThrottle(fn, intervalMs) {
|
|
|
4707
4711
|
|
|
4708
4712
|
// src/thread-session.ts
|
|
4709
4713
|
var PATCH_INTERVAL_MS = 300;
|
|
4714
|
+
var NO_ACTIVITY_TIMEOUT_MS = 6e4;
|
|
4715
|
+
var NO_RESPONSE_MESSAGE = "(no response from the model backend \u2014 it may be unavailable or its API auth expired; nothing was changed, please retry)";
|
|
4710
4716
|
var ThreadSession = class {
|
|
4711
4717
|
constructor(deps) {
|
|
4712
4718
|
this.deps = deps;
|
|
@@ -4768,6 +4774,18 @@ var ThreadSession = class {
|
|
|
4768
4774
|
};
|
|
4769
4775
|
const { systemPrompt, tools } = this.deps.resolveConfig();
|
|
4770
4776
|
await this.backfillHistoryOnce(replyToMessageId, body);
|
|
4777
|
+
let sawActivity = false;
|
|
4778
|
+
let turnSettled = false;
|
|
4779
|
+
const settleOnce = () => {
|
|
4780
|
+
if (turnSettled) return true;
|
|
4781
|
+
turnSettled = true;
|
|
4782
|
+
return false;
|
|
4783
|
+
};
|
|
4784
|
+
const watchdog = setTimeout(() => {
|
|
4785
|
+
if (sawActivity || this.active !== turn || settleOnce()) return;
|
|
4786
|
+
this.deps.log(`turn watchdog: no model activity in ${NO_ACTIVITY_TIMEOUT_MS}ms \u2014 failing turn (room=${this.deps.roomId} thread=${this.deps.threadId})`);
|
|
4787
|
+
void this.failTurn(NO_RESPONSE_MESSAGE);
|
|
4788
|
+
}, NO_ACTIVITY_TIMEOUT_MS);
|
|
4771
4789
|
try {
|
|
4772
4790
|
const result = await runLoop({
|
|
4773
4791
|
config: this.deps.runtimeConfig,
|
|
@@ -4778,11 +4796,13 @@ var ThreadSession = class {
|
|
|
4778
4796
|
history: this.history,
|
|
4779
4797
|
handlers: {
|
|
4780
4798
|
onTextDelta: (delta) => {
|
|
4799
|
+
sawActivity = true;
|
|
4781
4800
|
if (!this.active) return;
|
|
4782
4801
|
this.active.accumulated += delta;
|
|
4783
4802
|
this.active.throttle.schedule();
|
|
4784
4803
|
},
|
|
4785
4804
|
onToolCall: ({ name }) => {
|
|
4805
|
+
sawActivity = true;
|
|
4786
4806
|
this.deps.log(`[${this.deps.roomId}/${this.deps.threadId.slice(0, 8)}] tool_call: ${name}`);
|
|
4787
4807
|
void setStatus(`\u{1F527} ${name}`);
|
|
4788
4808
|
},
|
|
@@ -4796,15 +4816,23 @@ var ThreadSession = class {
|
|
|
4796
4816
|
}
|
|
4797
4817
|
}
|
|
4798
4818
|
});
|
|
4819
|
+
clearTimeout(watchdog);
|
|
4820
|
+
if (settleOnce()) return;
|
|
4799
4821
|
this.history.push({ role: "user", content: body });
|
|
4800
4822
|
if (result.finalMessage) {
|
|
4801
4823
|
this.history.push({ role: "assistant", content: result.finalMessage });
|
|
4802
4824
|
}
|
|
4803
4825
|
if (result.status === "error") {
|
|
4804
4826
|
this.deps.log(`runtime done with status=error (room=${this.deps.roomId} thread=${this.deps.threadId})`);
|
|
4827
|
+
if (!turn.accumulated) {
|
|
4828
|
+
await this.failTurn("(the model run ended with an error and produced no output \u2014 please retry)");
|
|
4829
|
+
return;
|
|
4830
|
+
}
|
|
4805
4831
|
}
|
|
4806
4832
|
await this.endTurn();
|
|
4807
4833
|
} catch (err) {
|
|
4834
|
+
clearTimeout(watchdog);
|
|
4835
|
+
if (settleOnce()) return;
|
|
4808
4836
|
const message = err instanceof Error ? err.message : String(err);
|
|
4809
4837
|
this.deps.log(`runtime error (room=${this.deps.roomId} thread=${this.deps.threadId}): ${message}`);
|
|
4810
4838
|
await this.failTurn(`(runtime error: ${message})`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openape/ape-agent",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.2",
|
|
4
4
|
"description": "OpenApe agent runtime: per-agent process that connects to chat.openape.ai, runs the LLM loop with tools + cron tasks, and streams replies back to owners.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"ofetch": "^1.4.1",
|
|
24
24
|
"ws": "^8.18.0",
|
|
25
25
|
"yaml": "^2.8.0",
|
|
26
|
-
"@openape/
|
|
27
|
-
"@openape/
|
|
28
|
-
"@openape/
|
|
26
|
+
"@openape/apes": "1.30.0",
|
|
27
|
+
"@openape/prompt-injection-detector": "0.1.0",
|
|
28
|
+
"@openape/cli-auth": "0.5.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@antfu/eslint-config": "^7.6.1",
|