@openape/ape-agent 2.2.0 → 2.4.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 +90 -29
- package/package.json +1 -1
package/dist/bridge.mjs
CHANGED
|
@@ -1353,11 +1353,12 @@ var ChatApi = class {
|
|
|
1353
1353
|
this.bearer = bearer;
|
|
1354
1354
|
}
|
|
1355
1355
|
async postMessage(roomId, body, opts = {}) {
|
|
1356
|
-
const
|
|
1356
|
+
const bodyForServer = opts.streaming ? body : clamp(body, MAX_BODY);
|
|
1357
1357
|
const url = `${this.endpoint}/api/rooms/${encodeURIComponent(roomId)}/messages`;
|
|
1358
|
-
const payload = { body:
|
|
1358
|
+
const payload = { body: bodyForServer };
|
|
1359
1359
|
if (opts.replyTo) payload.reply_to = opts.replyTo;
|
|
1360
1360
|
if (opts.threadId) payload.thread_id = opts.threadId;
|
|
1361
|
+
if (opts.streaming) payload.streaming = true;
|
|
1361
1362
|
const result = await ofetch5(url, {
|
|
1362
1363
|
method: "POST",
|
|
1363
1364
|
headers: { Authorization: await this.bearer() },
|
|
@@ -1401,13 +1402,32 @@ var ChatApi = class {
|
|
|
1401
1402
|
body: { name: name.slice(0, 100) }
|
|
1402
1403
|
});
|
|
1403
1404
|
}
|
|
1404
|
-
|
|
1405
|
-
|
|
1405
|
+
/**
|
|
1406
|
+
* Update an in-flight or completed message. The server differentiates
|
|
1407
|
+
* three modes via the message's current `streaming` state and the
|
|
1408
|
+
* `streaming` field in this call:
|
|
1409
|
+
*
|
|
1410
|
+
* - Stream tick: pass `body` only (current accumulated text).
|
|
1411
|
+
* Server keeps streaming=true and does NOT bump edited_at.
|
|
1412
|
+
* - Stream end: pass `body` + `streaming: false`. Server clears
|
|
1413
|
+
* the streaming flag and triggers the user-facing push.
|
|
1414
|
+
* - Tool-call status: pass `streamingStatus` only (no body).
|
|
1415
|
+
* Renders as "🔧 time.now" in the typing-subtitle.
|
|
1416
|
+
* - Tool-call cleared: pass `streamingStatus: null`.
|
|
1417
|
+
*/
|
|
1418
|
+
async patchMessage(messageId, opts = {}) {
|
|
1406
1419
|
const url = `${this.endpoint}/api/messages/${encodeURIComponent(messageId)}`;
|
|
1420
|
+
const payload = {};
|
|
1421
|
+
if (opts.body !== void 0) {
|
|
1422
|
+
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`;
|
|
1423
|
+
}
|
|
1424
|
+
if (opts.streaming !== void 0) payload.streaming = opts.streaming;
|
|
1425
|
+
if (opts.streamingStatus !== void 0) payload.streaming_status = opts.streamingStatus;
|
|
1426
|
+
if (Object.keys(payload).length === 0) return;
|
|
1407
1427
|
await ofetch5(url, {
|
|
1408
1428
|
method: "PATCH",
|
|
1409
1429
|
headers: { Authorization: await this.bearer() },
|
|
1410
|
-
body:
|
|
1430
|
+
body: payload
|
|
1411
1431
|
});
|
|
1412
1432
|
}
|
|
1413
1433
|
};
|
|
@@ -4021,14 +4041,14 @@ var PATCH_INTERVAL_MS = 300;
|
|
|
4021
4041
|
var ThreadSession = class {
|
|
4022
4042
|
constructor(deps) {
|
|
4023
4043
|
this.deps = deps;
|
|
4024
|
-
this.resolvedTools = taskTools(deps.tools);
|
|
4025
4044
|
}
|
|
4026
4045
|
active;
|
|
4027
4046
|
queue = [];
|
|
4028
4047
|
history = [];
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
* RPC-listener model where dispose() detached the listener.
|
|
4048
|
+
/**
|
|
4049
|
+
* No-op placeholder kept for API compatibility with the previous
|
|
4050
|
+
* RPC-listener model where dispose() detached the listener.
|
|
4051
|
+
*/
|
|
4032
4052
|
dispose() {
|
|
4033
4053
|
}
|
|
4034
4054
|
/** Forward an inbound chat message to the runtime. Queues if a turn is in flight. */
|
|
@@ -4040,9 +4060,10 @@ var ThreadSession = class {
|
|
|
4040
4060
|
void this.startTurn(body, replyToMessageId);
|
|
4041
4061
|
}
|
|
4042
4062
|
async startTurn(body, replyToMessageId) {
|
|
4043
|
-
const placeholder = await this.deps.chat.postMessage(this.deps.roomId, "
|
|
4063
|
+
const placeholder = await this.deps.chat.postMessage(this.deps.roomId, "", {
|
|
4044
4064
|
replyTo: replyToMessageId,
|
|
4045
|
-
threadId: this.deps.threadId
|
|
4065
|
+
threadId: this.deps.threadId,
|
|
4066
|
+
streaming: true
|
|
4046
4067
|
});
|
|
4047
4068
|
const turn = {
|
|
4048
4069
|
placeholderId: placeholder.id,
|
|
@@ -4050,21 +4071,30 @@ var ThreadSession = class {
|
|
|
4050
4071
|
replyToMessageId,
|
|
4051
4072
|
throttle: createThrottle(async () => {
|
|
4052
4073
|
if (!this.active || this.active.placeholderId !== placeholder.id) return;
|
|
4053
|
-
const text = this.active.accumulated
|
|
4074
|
+
const text = this.active.accumulated;
|
|
4075
|
+
if (text.length === 0) return;
|
|
4054
4076
|
try {
|
|
4055
|
-
await this.deps.chat.patchMessage(placeholder.id, text);
|
|
4077
|
+
await this.deps.chat.patchMessage(placeholder.id, { body: text });
|
|
4056
4078
|
} catch (err) {
|
|
4057
4079
|
this.deps.log(`patch failed (room=${this.deps.roomId} thread=${this.deps.threadId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
4058
4080
|
}
|
|
4059
4081
|
}, PATCH_INTERVAL_MS)
|
|
4060
4082
|
};
|
|
4061
4083
|
this.active = turn;
|
|
4084
|
+
const setStatus = async (status) => {
|
|
4085
|
+
try {
|
|
4086
|
+
await this.deps.chat.patchMessage(placeholder.id, { streamingStatus: status });
|
|
4087
|
+
} catch (err) {
|
|
4088
|
+
this.deps.log(`status patch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4089
|
+
}
|
|
4090
|
+
};
|
|
4091
|
+
const { systemPrompt, tools } = this.deps.resolveConfig();
|
|
4062
4092
|
try {
|
|
4063
4093
|
const result = await runLoop({
|
|
4064
4094
|
config: this.deps.runtimeConfig,
|
|
4065
|
-
systemPrompt
|
|
4095
|
+
systemPrompt,
|
|
4066
4096
|
userMessage: body,
|
|
4067
|
-
tools:
|
|
4097
|
+
tools: taskTools(tools),
|
|
4068
4098
|
maxSteps: this.deps.maxSteps,
|
|
4069
4099
|
history: this.history,
|
|
4070
4100
|
handlers: {
|
|
@@ -4075,12 +4105,15 @@ var ThreadSession = class {
|
|
|
4075
4105
|
},
|
|
4076
4106
|
onToolCall: ({ name }) => {
|
|
4077
4107
|
this.deps.log(`[${this.deps.roomId}/${this.deps.threadId.slice(0, 8)}] tool_call: ${name}`);
|
|
4108
|
+
void setStatus(`\u{1F527} ${name}`);
|
|
4078
4109
|
},
|
|
4079
4110
|
onToolResult: ({ name }) => {
|
|
4080
4111
|
this.deps.log(`[${this.deps.roomId}/${this.deps.threadId.slice(0, 8)}] tool_result: ${name}`);
|
|
4112
|
+
void setStatus(null);
|
|
4081
4113
|
},
|
|
4082
4114
|
onToolError: ({ name, error }) => {
|
|
4083
4115
|
this.deps.log(`[${this.deps.roomId}/${this.deps.threadId.slice(0, 8)}] tool_error: ${name} \u2192 ${error}`);
|
|
4116
|
+
void setStatus(null);
|
|
4084
4117
|
}
|
|
4085
4118
|
}
|
|
4086
4119
|
});
|
|
@@ -4091,28 +4124,51 @@ var ThreadSession = class {
|
|
|
4091
4124
|
if (result.status === "error") {
|
|
4092
4125
|
this.deps.log(`runtime done with status=error (room=${this.deps.roomId} thread=${this.deps.threadId})`);
|
|
4093
4126
|
}
|
|
4094
|
-
this.endTurn();
|
|
4127
|
+
await this.endTurn();
|
|
4095
4128
|
} catch (err) {
|
|
4096
4129
|
const message = err instanceof Error ? err.message : String(err);
|
|
4097
4130
|
this.deps.log(`runtime error (room=${this.deps.roomId} thread=${this.deps.threadId}): ${message}`);
|
|
4098
|
-
this.failTurn(`(runtime error: ${message})`);
|
|
4131
|
+
await this.failTurn(`(runtime error: ${message})`);
|
|
4099
4132
|
}
|
|
4100
4133
|
}
|
|
4101
|
-
|
|
4134
|
+
/**
|
|
4135
|
+
* Stream-end: flush any pending throttled body PATCH, then mark the
|
|
4136
|
+
* message as no-longer-streaming. The combined call also triggers
|
|
4137
|
+
* the user-facing push (the placeholder POST suppressed it).
|
|
4138
|
+
*/
|
|
4139
|
+
async endTurn() {
|
|
4102
4140
|
const turn = this.active;
|
|
4103
4141
|
if (!turn) return;
|
|
4104
4142
|
turn.throttle.flush();
|
|
4143
|
+
try {
|
|
4144
|
+
await this.deps.chat.patchMessage(turn.placeholderId, {
|
|
4145
|
+
body: turn.accumulated || "(empty response)",
|
|
4146
|
+
streaming: false,
|
|
4147
|
+
streamingStatus: null
|
|
4148
|
+
});
|
|
4149
|
+
} catch (err) {
|
|
4150
|
+
this.deps.log(`stream-end patch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4151
|
+
}
|
|
4105
4152
|
this.active = void 0;
|
|
4106
4153
|
const next = this.queue.shift();
|
|
4107
4154
|
if (next) {
|
|
4108
4155
|
void this.startTurn(next.body, next.replyToMessageId);
|
|
4109
4156
|
}
|
|
4110
4157
|
}
|
|
4111
|
-
failTurn(message) {
|
|
4158
|
+
async failTurn(message) {
|
|
4112
4159
|
const turn = this.active;
|
|
4113
4160
|
if (!turn) return;
|
|
4114
4161
|
turn.accumulated = message;
|
|
4115
4162
|
turn.throttle.flush();
|
|
4163
|
+
try {
|
|
4164
|
+
await this.deps.chat.patchMessage(turn.placeholderId, {
|
|
4165
|
+
body: message,
|
|
4166
|
+
streaming: false,
|
|
4167
|
+
streamingStatus: null
|
|
4168
|
+
});
|
|
4169
|
+
} catch (err) {
|
|
4170
|
+
this.deps.log(`fail-turn patch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4171
|
+
}
|
|
4116
4172
|
this.active = void 0;
|
|
4117
4173
|
const next = this.queue.shift();
|
|
4118
4174
|
if (next) {
|
|
@@ -4310,16 +4366,21 @@ var Bridge = class {
|
|
|
4310
4366
|
threadId,
|
|
4311
4367
|
chat: this.chat,
|
|
4312
4368
|
runtimeConfig: this.runtimeConfig(),
|
|
4313
|
-
//
|
|
4314
|
-
//
|
|
4315
|
-
// effect
|
|
4316
|
-
// SOUL.md + skills
|
|
4317
|
-
//
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4369
|
+
// Resolve tools + systemPrompt on every turn from agent.json
|
|
4370
|
+
// (latest sync from troop). Owner edits in the troop UI thus
|
|
4371
|
+
// take effect on the very next message in an existing thread —
|
|
4372
|
+
// not just on a freshly-opened one. SOUL.md + skills get merged
|
|
4373
|
+
// into the system prompt the same way.
|
|
4374
|
+
resolveConfig: () => {
|
|
4375
|
+
const tools = resolveTools(this.cfg.tools);
|
|
4376
|
+
return {
|
|
4377
|
+
tools,
|
|
4378
|
+
systemPrompt: composeSystemPrompt({
|
|
4379
|
+
base: resolveSystemPrompt(this.cfg.systemPrompt),
|
|
4380
|
+
enabledTools: tools
|
|
4381
|
+
})
|
|
4382
|
+
};
|
|
4383
|
+
},
|
|
4323
4384
|
maxSteps: this.cfg.maxSteps,
|
|
4324
4385
|
log
|
|
4325
4386
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openape/ape-agent",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.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",
|