@openape/ape-agent 2.6.2 → 2.7.0
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 +105 -17
- package/package.json +6 -5
package/dist/bridge.mjs
CHANGED
|
@@ -8,7 +8,7 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
|
8
8
|
var __esm = (fn, res) => function __init() {
|
|
9
9
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
10
|
};
|
|
11
|
-
var __commonJS = (cb, mod) => function
|
|
11
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
12
12
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
13
13
|
};
|
|
14
14
|
var __export = (target, all) => {
|
|
@@ -1032,13 +1032,6 @@ var require_shell_quote = __commonJS({
|
|
|
1032
1032
|
}
|
|
1033
1033
|
});
|
|
1034
1034
|
|
|
1035
|
-
// ../../packages/apes/dist/chunk-7OCVIDC7.js
|
|
1036
|
-
var init_chunk_7OCVIDC7 = __esm({
|
|
1037
|
-
"../../packages/apes/dist/chunk-7OCVIDC7.js"() {
|
|
1038
|
-
"use strict";
|
|
1039
|
-
}
|
|
1040
|
-
});
|
|
1041
|
-
|
|
1042
1035
|
// src/bridge.ts
|
|
1043
1036
|
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
|
|
1044
1037
|
import { homedir as homedir9 } from "os";
|
|
@@ -1340,6 +1333,66 @@ async function ensureFreshIdpAuth(now = Math.floor(Date.now() / 1e3)) {
|
|
|
1340
1333
|
return next;
|
|
1341
1334
|
}
|
|
1342
1335
|
|
|
1336
|
+
// ../../packages/prompt-injection-detector/dist/index.js
|
|
1337
|
+
var DEFAULT_THRESHOLD = 0.7;
|
|
1338
|
+
var DEFAULT_OWNER_THRESHOLD = 0.95;
|
|
1339
|
+
async function decide(detector, input, opts = {}) {
|
|
1340
|
+
const threshold = input.sender.isOwner ? opts.ownerThreshold ?? DEFAULT_OWNER_THRESHOLD : opts.threshold ?? DEFAULT_THRESHOLD;
|
|
1341
|
+
const result = await detector.classify(input);
|
|
1342
|
+
return {
|
|
1343
|
+
...result,
|
|
1344
|
+
threshold,
|
|
1345
|
+
blocked: result.score >= threshold
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
var PATTERNS = [
|
|
1349
|
+
// Instruction-override family. The defining phrase of prompt
|
|
1350
|
+
// injection — telling the model to discard its instructions in
|
|
1351
|
+
// favour of new ones.
|
|
1352
|
+
{ re: /\bignore (?:all |any |the |your )?(?:previous|prior|above|earlier|preceding) (?:instructions?|rules?|context|prompts?|messages?)\b/i, weight: 0.6, reason: "instruction-override" },
|
|
1353
|
+
{ re: /\bdisregard (?:all |any |the |your )?(?:previous|prior|above|earlier|preceding)?\s*(?:instructions?|rules?|context)\b/i, weight: 0.6, reason: "instruction-override" },
|
|
1354
|
+
{ re: /\b(?:you are|act as|pretend to be|roleplay as) (?:now |a |an )?(?:different|new|unrestricted|jailbroken|dan|do anything now)\b/i, weight: 0.55, reason: "role-override" },
|
|
1355
|
+
{ re: /\b(?:forget|drop|reset) (?:everything|all|your) (?:above|prior|previous|instructions?|rules?|context)\b/i, weight: 0.55, reason: "context-reset" },
|
|
1356
|
+
// Filesystem-exfiltration. Specific paths that have no business
|
|
1357
|
+
// appearing in normal chat — auth tokens, SSH keys, agent config.
|
|
1358
|
+
// `\b` would fail on `/etc/passwd` (slash is non-word, no boundary
|
|
1359
|
+
// with preceding space) — match the literal forms instead.
|
|
1360
|
+
{ re: /(?:~\/\.config\/apes|~\/\.openape|~\/\.ssh|\/etc\/passwd|\/etc\/shadow|\bid_rsa\b|\bid_ed25519\b|\bauth\.json\b|\.env(?:\.[\w-]+)?\b)/i, weight: 0.45, reason: "sensitive-path" },
|
|
1361
|
+
// Tool-call coercion. Phrases that try to talk the agent into
|
|
1362
|
+
// executing tools or running shell commands as part of the reply.
|
|
1363
|
+
{ re: /\b(?:run|execute|invoke|call)\s+(?:the\s+)?(?:shell|bash|sh|cmd|powershell|tool|command|script)\b/i, weight: 0.35, reason: "tool-coercion" },
|
|
1364
|
+
{ re: /\b(?:and\s+)?(?:post|send|share|paste|return|reply with|output)\s+(?:the\s+)?(?:contents?|output|result|file|secret|token|api[-_ ]?key)\b/i, weight: 0.3, reason: "exfil-request" },
|
|
1365
|
+
// Override + override-and-do (combined "do X without telling Y" forms).
|
|
1366
|
+
{ re: /\bwithout (?:telling|asking|informing|notifying|consulting|the consent of)\b/i, weight: 0.4, reason: "covert-action" },
|
|
1367
|
+
// System-prompt extraction.
|
|
1368
|
+
{ re: /\b(?:show|print|reveal|repeat|tell me|what is|what's) (?:your |the )?(?:system prompt|initial prompt|instructions|rules|directives|guidelines)\b/i, weight: 0.5, reason: "prompt-extraction" },
|
|
1369
|
+
// Encoding-based bypass attempts.
|
|
1370
|
+
{ re: /\b(?:base64|rot13|decode|decrypt) (?:this|the following|below)\b/i, weight: 0.3, reason: "encoding-bypass" }
|
|
1371
|
+
];
|
|
1372
|
+
function classifyHeuristic(input) {
|
|
1373
|
+
const text = input.text;
|
|
1374
|
+
let total = 0;
|
|
1375
|
+
const reasons = [];
|
|
1376
|
+
for (const p of PATTERNS) {
|
|
1377
|
+
if (p.re.test(text)) {
|
|
1378
|
+
total += p.weight;
|
|
1379
|
+
if (!reasons.includes(p.reason)) reasons.push(p.reason);
|
|
1380
|
+
if (total >= 1) break;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
const score = Math.min(1, total);
|
|
1384
|
+
return {
|
|
1385
|
+
score,
|
|
1386
|
+
backend: "heuristic",
|
|
1387
|
+
...reasons.length > 0 ? { reason: reasons.join(", ") } : {}
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
function createHeuristicDetector() {
|
|
1391
|
+
return {
|
|
1392
|
+
classify: async (input) => classifyHeuristic(input)
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1343
1396
|
// src/bridge.ts
|
|
1344
1397
|
import { decodeJwt } from "jose";
|
|
1345
1398
|
import WebSocket from "ws";
|
|
@@ -1352,6 +1405,8 @@ var ChatApi = class {
|
|
|
1352
1405
|
this.endpoint = endpoint;
|
|
1353
1406
|
this.bearer = bearer;
|
|
1354
1407
|
}
|
|
1408
|
+
endpoint;
|
|
1409
|
+
bearer;
|
|
1355
1410
|
async postMessage(roomId, body, opts = {}) {
|
|
1356
1411
|
const bodyForServer = opts.streaming ? body : clamp(body, MAX_BODY);
|
|
1357
1412
|
const url = `${this.endpoint}/api/rooms/${encodeURIComponent(roomId)}/messages`;
|
|
@@ -1455,7 +1510,7 @@ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as read
|
|
|
1455
1510
|
import { homedir as homedir6 } from "os";
|
|
1456
1511
|
import { join as join6 } from "path";
|
|
1457
1512
|
|
|
1458
|
-
// ../../packages/apes/dist/chunk-
|
|
1513
|
+
// ../../packages/apes/dist/chunk-L2V3CW5B.js
|
|
1459
1514
|
import { spawn } from "child_process";
|
|
1460
1515
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1461
1516
|
import { homedir as homedir3 } from "os";
|
|
@@ -1983,6 +2038,7 @@ import { basename, join as join4 } from "path";
|
|
|
1983
2038
|
|
|
1984
2039
|
// ../../packages/core/dist/index.js
|
|
1985
2040
|
import * as jose from "jose";
|
|
2041
|
+
var HKDF_INFO = new TextEncoder().encode("openape-sealed-box-v1");
|
|
1986
2042
|
|
|
1987
2043
|
// ../../packages/grants/dist/index.js
|
|
1988
2044
|
function normalizeSelector(selector) {
|
|
@@ -3116,10 +3172,8 @@ var consola = createConsola2();
|
|
|
3116
3172
|
// ../../packages/apes/dist/chunk-DYSFQ26B.js
|
|
3117
3173
|
var import_shell_quote = __toESM(require_shell_quote(), 1);
|
|
3118
3174
|
|
|
3119
|
-
// ../../node_modules/.pnpm/
|
|
3120
|
-
import "
|
|
3121
|
-
|
|
3122
|
-
// ../../node_modules/.pnpm/citty@0.1.6/node_modules/citty/dist/index.mjs
|
|
3175
|
+
// ../../node_modules/.pnpm/citty@0.2.2/node_modules/citty/dist/index.mjs
|
|
3176
|
+
import { parseArgs as parseArgs$1 } from "util";
|
|
3123
3177
|
function defineCommand(def) {
|
|
3124
3178
|
return def;
|
|
3125
3179
|
}
|
|
@@ -3528,13 +3582,12 @@ function extractOption(args, name) {
|
|
|
3528
3582
|
}
|
|
3529
3583
|
var AUTH_FILE2 = join5(homedir52(), ".config", "apes", "auth.json");
|
|
3530
3584
|
|
|
3531
|
-
// ../../packages/apes/dist/chunk-
|
|
3585
|
+
// ../../packages/apes/dist/chunk-4KPKANZT.js
|
|
3532
3586
|
init_chunk_OBF7IMQ2();
|
|
3533
3587
|
var debug = process.argv.includes("--debug");
|
|
3534
3588
|
|
|
3535
3589
|
// ../../packages/apes/dist/index.js
|
|
3536
3590
|
init_chunk_OBF7IMQ2();
|
|
3537
|
-
init_chunk_7OCVIDC7();
|
|
3538
3591
|
|
|
3539
3592
|
// src/cron-runner.ts
|
|
3540
3593
|
var TASK_CACHE_DIR = join6(homedir6(), ".openape", "agent", "tasks");
|
|
@@ -3608,6 +3661,7 @@ var CronRunner = class {
|
|
|
3608
3661
|
this.deps = deps;
|
|
3609
3662
|
this.loadTaskThreads();
|
|
3610
3663
|
}
|
|
3664
|
+
deps;
|
|
3611
3665
|
timer;
|
|
3612
3666
|
lastTickedMinute;
|
|
3613
3667
|
/**
|
|
@@ -4075,6 +4129,7 @@ var ThreadSession = class {
|
|
|
4075
4129
|
constructor(deps) {
|
|
4076
4130
|
this.deps = deps;
|
|
4077
4131
|
}
|
|
4132
|
+
deps;
|
|
4078
4133
|
active;
|
|
4079
4134
|
queue = [];
|
|
4080
4135
|
history = [];
|
|
@@ -4337,6 +4392,12 @@ function sleep(ms) {
|
|
|
4337
4392
|
function truncate(s2, n2) {
|
|
4338
4393
|
return s2.length <= n2 ? s2 : `${s2.slice(0, n2 - 1)}\u2026`;
|
|
4339
4394
|
}
|
|
4395
|
+
function refusalText(reason) {
|
|
4396
|
+
const base = "I won't process this message \u2014 it looks like a prompt-injection attempt.";
|
|
4397
|
+
return reason ? `${base}
|
|
4398
|
+
|
|
4399
|
+
(matched: ${reason})` : base;
|
|
4400
|
+
}
|
|
4340
4401
|
var Bridge = class {
|
|
4341
4402
|
constructor(cfg, selfEmail, ownerEmail) {
|
|
4342
4403
|
this.cfg = cfg;
|
|
@@ -4357,6 +4418,9 @@ var Bridge = class {
|
|
|
4357
4418
|
});
|
|
4358
4419
|
this.cron.start();
|
|
4359
4420
|
}
|
|
4421
|
+
cfg;
|
|
4422
|
+
selfEmail;
|
|
4423
|
+
ownerEmail;
|
|
4360
4424
|
// Sessions keyed by `${roomId}:${threadId}`. Each ThreadSession holds
|
|
4361
4425
|
// its own message history and calls @openape/apes' runLoop directly
|
|
4362
4426
|
// (no stdio JSON-RPC subprocess — see thread-session.ts).
|
|
@@ -4364,6 +4428,10 @@ var Bridge = class {
|
|
|
4364
4428
|
chat;
|
|
4365
4429
|
bearer;
|
|
4366
4430
|
cron;
|
|
4431
|
+
// Prompt-injection gate (#277). Pure heuristic by default — pluggable
|
|
4432
|
+
// backend later. The bridge is the choke-point for every chat message
|
|
4433
|
+
// before it reaches the agent runtime, so this is the right place.
|
|
4434
|
+
injectionDetector = createHeuristicDetector();
|
|
4367
4435
|
/**
|
|
4368
4436
|
* RuntimeConfig is shared across thread sessions and the cron runner.
|
|
4369
4437
|
* The bridge resolves it from its own env at boot and reuses for the
|
|
@@ -4414,7 +4482,7 @@ var Bridge = class {
|
|
|
4414
4482
|
if (accepted.length > 0) log(`accepted: ${accepted.join(", ")}`);
|
|
4415
4483
|
if (skipped.length > 0) log(`skipped (not on allowlist): ${skipped.join(", ")}`);
|
|
4416
4484
|
}
|
|
4417
|
-
handleInbound(msg) {
|
|
4485
|
+
async handleInbound(msg) {
|
|
4418
4486
|
if (msg.senderEmail === this.selfEmail) return;
|
|
4419
4487
|
if (!msg.body.trim()) return;
|
|
4420
4488
|
if (this.cfg.roomFilter && msg.roomId !== this.cfg.roomFilter) return;
|
|
@@ -4423,6 +4491,26 @@ var Bridge = class {
|
|
|
4423
4491
|
return;
|
|
4424
4492
|
}
|
|
4425
4493
|
log(`[${msg.roomId}/${msg.threadId.slice(0, 8)}] in: ${truncate(msg.body, 80)}`);
|
|
4494
|
+
const decision = await decide(this.injectionDetector, {
|
|
4495
|
+
text: msg.body,
|
|
4496
|
+
sender: {
|
|
4497
|
+
email: msg.senderEmail,
|
|
4498
|
+
isOwner: msg.senderEmail === this.ownerEmail
|
|
4499
|
+
}
|
|
4500
|
+
});
|
|
4501
|
+
if (decision.blocked) {
|
|
4502
|
+
log(`[${msg.roomId}/${msg.threadId.slice(0, 8)}] BLOCKED prompt-injection (score=${decision.score.toFixed(2)}, reason=${decision.reason ?? "n/a"})`);
|
|
4503
|
+
try {
|
|
4504
|
+
await this.chat.postMessage(msg.roomId, refusalText(decision.reason), {
|
|
4505
|
+
replyTo: msg.id,
|
|
4506
|
+
threadId: msg.threadId
|
|
4507
|
+
});
|
|
4508
|
+
} catch (err) {
|
|
4509
|
+
const m2 = err instanceof Error ? err.message : String(err);
|
|
4510
|
+
log(`[${msg.roomId}] failed to post refusal: ${m2}`);
|
|
4511
|
+
}
|
|
4512
|
+
return;
|
|
4513
|
+
}
|
|
4426
4514
|
const session = this.getOrCreateThread(msg.roomId, msg.threadId);
|
|
4427
4515
|
session.enqueue(msg.body, msg.id);
|
|
4428
4516
|
}
|
|
@@ -4489,7 +4577,7 @@ var Bridge = class {
|
|
|
4489
4577
|
return;
|
|
4490
4578
|
}
|
|
4491
4579
|
if (frame.type !== "message") return;
|
|
4492
|
-
this.handleInbound(frame.payload);
|
|
4580
|
+
void this.handleInbound(frame.payload);
|
|
4493
4581
|
});
|
|
4494
4582
|
ws.on("close", () => {
|
|
4495
4583
|
if (pingTimer) clearInterval(pingTimer);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openape/ape-agent",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
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,17 +23,18 @@
|
|
|
23
23
|
"ofetch": "^1.4.1",
|
|
24
24
|
"ws": "^8.18.0",
|
|
25
25
|
"yaml": "^2.8.0",
|
|
26
|
-
"@openape/apes": "1.
|
|
27
|
-
"@openape/
|
|
26
|
+
"@openape/apes": "1.25.1",
|
|
27
|
+
"@openape/prompt-injection-detector": "0.1.0",
|
|
28
|
+
"@openape/cli-auth": "0.4.1"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@antfu/eslint-config": "^7.6.1",
|
|
31
32
|
"@types/node": "^22.19.13",
|
|
32
33
|
"@types/ws": "^8.5.13",
|
|
33
|
-
"eslint": "^
|
|
34
|
+
"eslint": "^10.4.0",
|
|
34
35
|
"tsup": "^8.5.1",
|
|
35
36
|
"typescript": "^5.9.3",
|
|
36
|
-
"vitest": "^
|
|
37
|
+
"vitest": "^4.1.7"
|
|
37
38
|
},
|
|
38
39
|
"engines": {
|
|
39
40
|
"node": ">=22"
|