@openape/ape-agent 2.8.15 → 2.9.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.
Files changed (2) hide show
  1. package/dist/bridge.mjs +16 -154
  2. package/package.json +3 -3
package/dist/bridge.mjs CHANGED
@@ -1398,117 +1398,9 @@ function createHeuristicDetector() {
1398
1398
  import { decodeJwt } from "jose";
1399
1399
  import WebSocket from "ws";
1400
1400
 
1401
- // src/chat-api.ts
1402
- import { ofetch as ofetch6 } from "ofetch";
1403
- var MAX_BODY = 1e4;
1404
- var ChatApi = class {
1405
- constructor(endpoint, bearer) {
1406
- this.endpoint = endpoint;
1407
- this.bearer = bearer;
1408
- }
1409
- endpoint;
1410
- bearer;
1411
- async postMessage(roomId, body, opts = {}) {
1412
- const bodyForServer = opts.streaming ? body : clamp(body, MAX_BODY);
1413
- const url = `${this.endpoint}/api/rooms/${encodeURIComponent(roomId)}/messages`;
1414
- const payload = { body: bodyForServer };
1415
- if (opts.replyTo) payload.reply_to = opts.replyTo;
1416
- if (opts.threadId) payload.thread_id = opts.threadId;
1417
- if (opts.streaming) payload.streaming = true;
1418
- const result = await ofetch6(url, {
1419
- method: "POST",
1420
- headers: { Authorization: await this.bearer() },
1421
- body: payload
1422
- });
1423
- return result;
1424
- }
1425
- /**
1426
- * Fetch the most recent `limit` messages in a thread, oldest-first.
1427
- * Used by ThreadSession to seed `history` so the agent has the
1428
- * full conversation context after a bridge restart — otherwise it
1429
- * only sees messages that arrived via WS since the process boot.
1430
- */
1431
- async listMessages(roomId, threadId, limit = 50) {
1432
- const url = `${this.endpoint}/api/rooms/${encodeURIComponent(roomId)}/messages?thread_id=${encodeURIComponent(threadId)}&limit=${limit}`;
1433
- return await ofetch6(url, {
1434
- method: "GET",
1435
- headers: { Authorization: await this.bearer() }
1436
- });
1437
- }
1438
- async requestContact(peerEmail) {
1439
- const url = `${this.endpoint}/api/contacts`;
1440
- return await ofetch6(url, {
1441
- method: "POST",
1442
- headers: { Authorization: await this.bearer() },
1443
- body: { email: peerEmail }
1444
- });
1445
- }
1446
- async listContacts() {
1447
- const url = `${this.endpoint}/api/contacts`;
1448
- return await ofetch6(url, {
1449
- method: "GET",
1450
- headers: { Authorization: await this.bearer() }
1451
- });
1452
- }
1453
- async acceptContact(peerEmail) {
1454
- const url = `${this.endpoint}/api/contacts/${encodeURIComponent(peerEmail)}/accept`;
1455
- return await ofetch6(url, {
1456
- method: "POST",
1457
- headers: { Authorization: await this.bearer() }
1458
- });
1459
- }
1460
- /**
1461
- * Create a named thread in a room. Used by the cron-runner to drop
1462
- * each task's runs into its own thread so the chat sidebar shows
1463
- * one thread per task instead of all task DMs piling into the
1464
- * main thread.
1465
- */
1466
- async createThread(roomId, name) {
1467
- const url = `${this.endpoint}/api/rooms/${encodeURIComponent(roomId)}/threads`;
1468
- return await ofetch6(url, {
1469
- method: "POST",
1470
- headers: { Authorization: await this.bearer() },
1471
- body: { name: name.slice(0, 100) }
1472
- });
1473
- }
1474
- /**
1475
- * Update an in-flight or completed message. The server differentiates
1476
- * three modes via the message's current `streaming` state and the
1477
- * `streaming` field in this call:
1478
- *
1479
- * - Stream tick: pass `body` only (current accumulated text).
1480
- * Server keeps streaming=true and does NOT bump edited_at.
1481
- * - Stream end: pass `body` + `streaming: false`. Server clears
1482
- * the streaming flag and triggers the user-facing push.
1483
- * - Tool-call status: pass `streamingStatus` only (no body).
1484
- * Renders as "🔧 time.now" in the typing-subtitle.
1485
- * - Tool-call cleared: pass `streamingStatus: null`.
1486
- */
1487
- async patchMessage(messageId, opts = {}) {
1488
- const url = `${this.endpoint}/api/messages/${encodeURIComponent(messageId)}`;
1489
- const payload = {};
1490
- if (opts.body !== void 0) {
1491
- payload.body = opts.streaming === false && opts.body.trim().length === 0 ? clamp(opts.body, MAX_BODY) : opts.body.length <= MAX_BODY ? opts.body : `${opts.body.slice(0, MAX_BODY - 1)}\u2026`;
1492
- }
1493
- if (opts.streaming !== void 0) payload.streaming = opts.streaming;
1494
- if (opts.streamingStatus !== void 0) payload.streaming_status = opts.streamingStatus;
1495
- if (Object.keys(payload).length === 0) return;
1496
- await ofetch6(url, {
1497
- method: "PATCH",
1498
- headers: { Authorization: await this.bearer() },
1499
- body: payload
1500
- });
1501
- }
1502
- };
1503
- function clamp(s2, max) {
1504
- if (s2.trim().length === 0) return "\u2026";
1505
- if (s2.length <= max) return s2;
1506
- return `${s2.slice(0, max - 1)}\u2026`;
1507
- }
1508
-
1509
1401
  // src/troop-chat-api.ts
1510
- import { ofetch as ofetch7 } from "ofetch";
1511
- var MAX_BODY2 = 64 * 1024;
1402
+ import { ofetch as ofetch6 } from "ofetch";
1403
+ var MAX_BODY = 64 * 1024;
1512
1404
  var SYNTHETIC_THREAD_ID = "main";
1513
1405
  function asHistory(msg, agentEmail, ownerEmail) {
1514
1406
  return {
@@ -1542,7 +1434,7 @@ var TroopChatApi = class {
1542
1434
  /** Resolve + cache the agent's chat row (lazy fetch on first use). */
1543
1435
  async getBootstrap() {
1544
1436
  if (this.bootstrap) return this.bootstrap;
1545
- this.bootstrap = await ofetch7(`${this.endpoint}/api/agents/me/chat`, {
1437
+ this.bootstrap = await ofetch6(`${this.endpoint}/api/agents/me/chat`, {
1546
1438
  method: "GET",
1547
1439
  headers: { Authorization: await this.bearer() }
1548
1440
  });
@@ -1557,11 +1449,11 @@ var TroopChatApi = class {
1557
1449
  void roomId;
1558
1450
  void opts.threadId;
1559
1451
  const payload = {
1560
- body: body.length > MAX_BODY2 ? `${body.slice(0, MAX_BODY2 - 1)}\u2026` : body
1452
+ body: body.length > MAX_BODY ? `${body.slice(0, MAX_BODY - 1)}\u2026` : body
1561
1453
  };
1562
1454
  if (opts.replyTo) payload.reply_to = opts.replyTo;
1563
1455
  if (opts.streaming) payload.streaming = true;
1564
- const msg = await ofetch7(`${this.endpoint}/api/agents/me/chat/messages`, {
1456
+ const msg = await ofetch6(`${this.endpoint}/api/agents/me/chat/messages`, {
1565
1457
  method: "POST",
1566
1458
  headers: { Authorization: await this.bearer() },
1567
1459
  body: payload
@@ -1572,7 +1464,7 @@ var TroopChatApi = class {
1572
1464
  void roomId;
1573
1465
  void threadId;
1574
1466
  void limit;
1575
- const fresh = await ofetch7(`${this.endpoint}/api/agents/me/chat`, {
1467
+ const fresh = await ofetch6(`${this.endpoint}/api/agents/me/chat`, {
1576
1468
  method: "GET",
1577
1469
  headers: { Authorization: await this.bearer() }
1578
1470
  });
@@ -1582,12 +1474,12 @@ var TroopChatApi = class {
1582
1474
  async patchMessage(messageId, opts = {}) {
1583
1475
  const payload = {};
1584
1476
  if (opts.body !== void 0) {
1585
- payload.body = opts.body.length > MAX_BODY2 ? `${opts.body.slice(0, MAX_BODY2 - 1)}\u2026` : opts.body;
1477
+ payload.body = opts.body.length > MAX_BODY ? `${opts.body.slice(0, MAX_BODY - 1)}\u2026` : opts.body;
1586
1478
  }
1587
1479
  if (opts.streaming !== void 0) payload.streaming = opts.streaming;
1588
1480
  if (opts.streamingStatus !== void 0) payload.streaming_status = opts.streamingStatus;
1589
1481
  if (Object.keys(payload).length === 0) return;
1590
- await ofetch7(`${this.endpoint}/api/agents/me/chat/messages/${encodeURIComponent(messageId)}`, {
1482
+ await ofetch6(`${this.endpoint}/api/agents/me/chat/messages/${encodeURIComponent(messageId)}`, {
1591
1483
  method: "PATCH",
1592
1484
  headers: { Authorization: await this.bearer() },
1593
1485
  body: payload
@@ -5014,7 +4906,7 @@ function resolveTools(envFallback) {
5014
4906
  }
5015
4907
  return envFallback;
5016
4908
  }
5017
- var DEFAULT_ENDPOINT = "https://chat.openape.ai";
4909
+ var DEFAULT_ENDPOINT = "https://troop.openape.ai";
5018
4910
  var DEFAULT_APES_BIN = "apes";
5019
4911
  var DEFAULT_MAX_STEPS = 10;
5020
4912
  var DEFAULT_SYSTEM_PROMPT = `You are a helpful assistant in a 1:1 chat. Be concise and friendly. When asked for facts, say "I don't know" rather than guess.`;
@@ -5022,28 +4914,7 @@ var PING_INTERVAL_MS = 3e4;
5022
4914
  var RECONNECT_BASE_MS = 1e3;
5023
4915
  var RECONNECT_MAX_MS = 3e4;
5024
4916
  var ALLOWLIST_POLL_INTERVAL_MS = 3e4;
5025
- function loadBridgeEnvFile() {
5026
- const path = join8(homedir9(), "Library", "Application Support", "openape", "bridge", ".env");
5027
- if (!existsSync6(path)) return;
5028
- try {
5029
- const raw = readFileSync8(path, "utf8");
5030
- for (const line of raw.split(/\r?\n/)) {
5031
- const trimmed = line.trim();
5032
- if (!trimmed || trimmed.startsWith("#")) continue;
5033
- const eq = trimmed.indexOf("=");
5034
- if (eq < 0) continue;
5035
- const key = trimmed.slice(0, eq).trim();
5036
- const value = trimmed.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
5037
- if (!key) continue;
5038
- if (process3.env[key] === void 0) {
5039
- process3.env[key] = value;
5040
- }
5041
- }
5042
- } catch {
5043
- }
5044
- }
5045
4917
  function readConfig() {
5046
- loadBridgeEnvFile();
5047
4918
  const toolsRaw = process3.env.APE_CHAT_BRIDGE_TOOLS ?? "";
5048
4919
  const tools = toolsRaw.split(",").map((s2) => s2.trim()).filter(Boolean);
5049
4920
  const maxStepsRaw = process3.env.APE_CHAT_BRIDGE_MAX_STEPS;
@@ -5051,20 +4922,17 @@ function readConfig() {
5051
4922
  const model = process3.env.APE_CHAT_BRIDGE_MODEL;
5052
4923
  if (!model) {
5053
4924
  throw new Error(
5054
- "APE_CHAT_BRIDGE_MODEL is not set. Set it in the bridge .env (usually `~/Library/Application Support/openape/bridge/.env` on macOS) or globally in `~/litellm/.env` so resolveBridgeConfig picks it up at spawn time. Common values: `gpt-5.4` (ChatGPT-only LiteLLM proxy), `claude-haiku-4-5` (Anthropic-only)."
4925
+ "APE_CHAT_BRIDGE_MODEL is not set. Set it in the container env (compose environment: block) or globally in `~/litellm/.env`. Common values: `gpt-5.4` (ChatGPT-only LiteLLM proxy), `claude-haiku-4-5` (Anthropic-only)."
5055
4926
  );
5056
4927
  }
5057
- const targetRaw = (process3.env.OPENAPE_BRIDGE_TARGET ?? "chat").toLowerCase();
5058
- const target = targetRaw === "troop" ? "troop" : "chat";
5059
4928
  return {
5060
- endpoint: (process3.env.APE_CHAT_ENDPOINT ?? DEFAULT_ENDPOINT).replace(/\/$/, ""),
4929
+ endpoint: (process3.env.OPENAPE_TROOP_URL ?? DEFAULT_ENDPOINT).replace(/\/$/, ""),
5061
4930
  apesBin: process3.env.APE_CHAT_BRIDGE_APES_BIN ?? DEFAULT_APES_BIN,
5062
4931
  model,
5063
4932
  systemPrompt: process3.env.APE_CHAT_BRIDGE_SYSTEM_PROMPT ?? DEFAULT_SYSTEM_PROMPT,
5064
4933
  tools,
5065
4934
  maxSteps: Number.isFinite(maxSteps) && maxSteps > 0 ? maxSteps : DEFAULT_MAX_STEPS,
5066
- roomFilter: process3.env.APE_CHAT_BRIDGE_ROOM,
5067
- target
4935
+ roomFilter: process3.env.APE_CHAT_BRIDGE_ROOM
5068
4936
  };
5069
4937
  }
5070
4938
  async function getIdentity() {
@@ -5100,13 +4968,13 @@ var Bridge = class {
5100
4968
  const idp = await ensureFreshIdpAuth();
5101
4969
  return `Bearer ${idp.access_token}`;
5102
4970
  };
5103
- this.chat = this.cfg.target === "troop" ? new TroopChatApi(this.cfg.endpoint, this.bearer) : new ChatApi(this.cfg.endpoint, this.bearer);
4971
+ this.chat = new TroopChatApi(this.cfg.endpoint, this.bearer);
5104
4972
  this.cron = new CronRunner({
5105
4973
  runtimeConfig: this.runtimeConfig(),
5106
4974
  chat: this.chat,
5107
4975
  ownerEmail: this.ownerEmail,
5108
4976
  log,
5109
- troopUrl: (process3.env.OPENAPE_TROOP_URL ?? "https://troop.openape.ai").replace(/\/$/, ""),
4977
+ troopUrl: this.cfg.endpoint,
5110
4978
  bearer: this.bearer
5111
4979
  });
5112
4980
  this.cron.start();
@@ -5118,11 +4986,6 @@ var Bridge = class {
5118
4986
  // its own message history and calls @openape/apes' runLoop directly
5119
4987
  // (no stdio JSON-RPC subprocess — see thread-session.ts).
5120
4988
  threads = /* @__PURE__ */ new Map();
5121
- // ChatApi and TroopChatApi expose the same surface (postMessage /
5122
- // listMessages / patchMessage / listContacts / requestContact /
5123
- // acceptContact / createThread) so the rest of the bridge calls
5124
- // through a structurally-typed reference without caring which
5125
- // backend is in play. Picked at construction time from cfg.target.
5126
4989
  chat;
5127
4990
  bearer;
5128
4991
  cron;
@@ -5268,8 +5131,7 @@ var Bridge = class {
5268
5131
  }
5269
5132
  async pumpOnce() {
5270
5133
  const bearer = await this.bearer();
5271
- const wsPath = this.cfg.target === "troop" ? "/_ws/chat" : "/api/ws";
5272
- const wsUrl = `${this.cfg.endpoint.replace(/^http/, "ws")}${wsPath}?token=${encodeURIComponent(bearer.replace(/^Bearer\s+/i, ""))}`;
5134
+ const wsUrl = `${this.cfg.endpoint.replace(/^http/, "ws")}/_ws/chat?token=${encodeURIComponent(bearer.replace(/^Bearer\s+/i, ""))}`;
5273
5135
  const ws = new WebSocket(wsUrl);
5274
5136
  return new Promise((resolve4, reject) => {
5275
5137
  let pingTimer;
@@ -5299,7 +5161,7 @@ var Bridge = class {
5299
5161
  return;
5300
5162
  }
5301
5163
  if (frame.type !== "message" || !frame.payload) return;
5302
- const msg = this.cfg.target === "troop" ? this.translateTroopPayload(frame.chat_id ?? "", frame.payload) : frame.payload;
5164
+ const msg = this.translateTroopPayload(frame.chat_id ?? "", frame.payload);
5303
5165
  void this.handleInbound(msg);
5304
5166
  });
5305
5167
  ws.on("close", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openape/ape-agent",
3
- "version": "2.8.15",
3
+ "version": "2.9.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,9 +23,9 @@
23
23
  "ofetch": "^1.4.1",
24
24
  "ws": "^8.18.0",
25
25
  "yaml": "^2.8.0",
26
+ "@openape/cli-auth": "0.5.0",
26
27
  "@openape/apes": "1.29.0",
27
- "@openape/prompt-injection-detector": "0.1.0",
28
- "@openape/cli-auth": "0.5.0"
28
+ "@openape/prompt-injection-detector": "0.1.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@antfu/eslint-config": "^7.6.1",