@parall/parall 1.30.0 → 1.32.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/accounts.d.ts +1 -1
- package/dist/accounts.js +4 -4
- package/dist/channel.d.ts +2 -2
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js +10 -10
- package/dist/config-manager.d.ts +1 -1
- package/dist/config-manager.d.ts.map +1 -1
- package/dist/config-manager.js +70 -30
- package/dist/fork.d.ts.map +1 -1
- package/dist/fork.js +17 -17
- package/dist/gateway.d.ts +3 -3
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +214 -171
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +52 -26
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/oc-session.d.ts.map +1 -1
- package/dist/oc-session.js +35 -27
- package/dist/outbound.d.ts +2 -2
- package/dist/outbound.d.ts.map +1 -1
- package/dist/outbound.js +13 -14
- package/dist/routing.d.ts +2 -2
- package/dist/routing.js +1 -1
- package/dist/runtime.d.ts +5 -5
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +2 -2
- package/dist/session.js +4 -4
- package/dist/wiki-helper.d.ts.map +1 -1
- package/dist/wiki-helper.js +27 -27
- package/openclaw.plugin.json +4 -1
- package/package.json +3 -3
- package/skills/parall-clips/SKILL.md +48 -0
- package/src/accounts.ts +5 -5
- package/src/channel.ts +15 -14
- package/src/config-manager.ts +79 -34
- package/src/fork.ts +26 -22
- package/src/gateway.ts +245 -175
- package/src/hooks.ts +109 -50
- package/src/index.ts +8 -8
- package/src/oc-session.ts +43 -35
- package/src/outbound.ts +18 -17
- package/src/routing.ts +2 -2
- package/src/runtime.ts +11 -7
- package/src/session.ts +4 -4
- package/src/wiki-helper.ts +47 -34
package/dist/gateway.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { ParallAgentGateway, parseShutdownDeadlineMs, } from
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
1
|
+
import { ParallAgentGateway, parseShutdownDeadlineMs, parseForkDeadlineMs, parseDispatchDeadlineMs, initAgentTelemetry, createOtelLogger, } from '@parall/agent-core';
|
|
2
|
+
import { appendPreparedLocalAttachmentRefs, ensureLocalAttachmentGitExclude, pinLocalAttachmentPaths, } from '@parall/agent-core/internal/attachment-input';
|
|
3
|
+
import { ApiError, ParallClient, ParallWs } from '@parall/sdk';
|
|
4
|
+
import * as crypto from 'node:crypto';
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import { resolveParallAccount } from './accounts.js';
|
|
8
|
+
import { getParallRuntime, removeParallAccountState, setAgentIdentity, setAgentSessionBinding, setDispatchGroupKey, setParallAccountState, } from './runtime.js';
|
|
9
|
+
import { buildOrchestratorSessionKey } from './session.js';
|
|
10
|
+
import { fetchAndApplyPlatformConfig } from './config-manager.js';
|
|
11
|
+
import { startWikiHelper } from './wiki-helper.js';
|
|
12
|
+
import { SessionManager } from './oc-session.js';
|
|
13
|
+
import { cleanupForkSession, clearSessionState, forkOrchestratorSession, resolveSessionId, resolveTranscriptFile, } from './fork.js';
|
|
13
14
|
function resolveWsUrl(account) {
|
|
14
15
|
if (account.config.ws_url)
|
|
15
16
|
return account.config.ws_url;
|
|
16
|
-
const base = account.config.parall_url.replace(/\/$/,
|
|
17
|
-
const wsBase = base.replace(/^http/,
|
|
17
|
+
const base = account.config.parall_url.replace(/\/$/, '');
|
|
18
|
+
const wsBase = base.replace(/^http/, 'ws');
|
|
18
19
|
return `${wsBase}/ws`;
|
|
19
20
|
}
|
|
20
21
|
async function getAgentMeWithLegacyFallback(client, orgId) {
|
|
@@ -42,15 +43,15 @@ function buildInboundCtx(core, accountId, event, sessionKey, bodyForAgent, earli
|
|
|
42
43
|
To: `parall:orchestrator`,
|
|
43
44
|
SessionKey: sessionKey,
|
|
44
45
|
AccountId: accountId,
|
|
45
|
-
ChatType: event.targetType ??
|
|
46
|
+
ChatType: event.targetType ?? 'unknown',
|
|
46
47
|
SenderName: event.senderName,
|
|
47
48
|
SenderId: event.senderId,
|
|
48
|
-
Provider:
|
|
49
|
-
Surface:
|
|
49
|
+
Provider: 'parall',
|
|
50
|
+
Surface: 'parall',
|
|
50
51
|
MessageSid: event.messageId,
|
|
51
52
|
Timestamp: Date.now(),
|
|
52
53
|
CommandAuthorized: true,
|
|
53
|
-
OriginatingChannel:
|
|
54
|
+
OriginatingChannel: 'parall',
|
|
54
55
|
OriginatingTo: `parall:orchestrator`,
|
|
55
56
|
});
|
|
56
57
|
}
|
|
@@ -64,13 +65,13 @@ function buildInboundHistory(events) {
|
|
|
64
65
|
meta.push(`[Thread: ${event.threadRootId}]`);
|
|
65
66
|
if (event.attachments?.length) {
|
|
66
67
|
for (const att of event.attachments) {
|
|
67
|
-
const safeMime = att.mimeType.replace(/[\r\n[\]|]/g,
|
|
68
|
+
const safeMime = att.mimeType.replace(/[\r\n[\]|]/g, ' ').trim();
|
|
68
69
|
meta.push(`[Attachment: prll://${att.id} | ${safeMime}]`);
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
return {
|
|
72
73
|
sender: event.senderName,
|
|
73
|
-
body: meta.length > 0 ? `${meta.join(
|
|
74
|
+
body: meta.length > 0 ? `${meta.join(' ')}\n${event.body}` : event.body,
|
|
74
75
|
};
|
|
75
76
|
});
|
|
76
77
|
}
|
|
@@ -145,69 +146,95 @@ async function waitForOpenClawSessionId(sessionsDir, sessionKey, timeoutMs = 10_
|
|
|
145
146
|
return null;
|
|
146
147
|
}
|
|
147
148
|
function createOpenClawDispatchAdapter(opts) {
|
|
149
|
+
const activeStreams = new Map();
|
|
148
150
|
return {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
cfg: opts.cfg,
|
|
156
|
-
dispatcherOptions: {
|
|
157
|
-
deliver: async (payload) => {
|
|
158
|
-
const replyText = payload.text?.trim();
|
|
159
|
-
if (!replyText)
|
|
160
|
-
return;
|
|
161
|
-
stream.push({
|
|
162
|
-
type: "text",
|
|
163
|
-
text: replyText,
|
|
164
|
-
project: false,
|
|
165
|
-
groupKey: turnGroupKey || undefined,
|
|
166
|
-
});
|
|
167
|
-
},
|
|
168
|
-
onReplyStart: () => {
|
|
169
|
-
turnGroupKey = crypto.randomUUID();
|
|
170
|
-
setDispatchGroupKey(sessionKey, turnGroupKey);
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
replyOptions: {
|
|
174
|
-
onReasoningStream: (payload) => {
|
|
175
|
-
reasoningBuffer += payload.text ?? "";
|
|
176
|
-
},
|
|
177
|
-
onReasoningEnd: async () => {
|
|
178
|
-
if (!reasoningBuffer)
|
|
179
|
-
return;
|
|
180
|
-
stream.push({
|
|
181
|
-
type: "thinking",
|
|
182
|
-
text: reasoningBuffer,
|
|
183
|
-
groupKey: turnGroupKey || undefined,
|
|
184
|
-
});
|
|
185
|
-
reasoningBuffer = "";
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
|
-
});
|
|
189
|
-
run.then(() => stream.end()).catch((err) => stream.fail(err));
|
|
190
|
-
let sessionId;
|
|
151
|
+
abortDispatch(sessionKey) {
|
|
152
|
+
activeStreams.get(sessionKey)?.fail(new Error('dispatch deadline exceeded'));
|
|
153
|
+
},
|
|
154
|
+
async *dispatch({ event, earlierEvents = [], bodyForAgent, sessionKey, context }) {
|
|
155
|
+
let promptBody = bodyForAgent;
|
|
156
|
+
let releasePreparedAttachments = () => { };
|
|
191
157
|
try {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
158
|
+
const prepared = await appendPreparedLocalAttachmentRefs(bodyForAgent, event, context, {
|
|
159
|
+
workspaceDir: opts.workspaceDir,
|
|
160
|
+
log: context.log,
|
|
161
|
+
});
|
|
162
|
+
promptBody = prepared.body;
|
|
163
|
+
releasePreparedAttachments = pinLocalAttachmentPaths(prepared.attachments.images);
|
|
196
164
|
}
|
|
197
|
-
catch {
|
|
198
|
-
|
|
165
|
+
catch (err) {
|
|
166
|
+
context.log?.warn?.(`failed to prepare local attachments: ${String(err)}`);
|
|
199
167
|
}
|
|
200
|
-
|
|
201
|
-
stream
|
|
168
|
+
try {
|
|
169
|
+
const stream = createRuntimeEventStream();
|
|
170
|
+
activeStreams.set(sessionKey, stream);
|
|
171
|
+
let reasoningBuffer = '';
|
|
172
|
+
let turnGroupKey = '';
|
|
173
|
+
const run = opts.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
174
|
+
ctx: buildInboundCtx(opts.core, opts.accountId, event, sessionKey, promptBody, earlierEvents),
|
|
175
|
+
cfg: opts.cfg,
|
|
176
|
+
dispatcherOptions: {
|
|
177
|
+
deliver: async (payload) => {
|
|
178
|
+
const replyText = payload.text?.trim();
|
|
179
|
+
if (!replyText)
|
|
180
|
+
return;
|
|
181
|
+
stream.push({
|
|
182
|
+
type: 'text',
|
|
183
|
+
text: replyText,
|
|
184
|
+
project: false,
|
|
185
|
+
groupKey: turnGroupKey || undefined,
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
onReplyStart: () => {
|
|
189
|
+
turnGroupKey = crypto.randomUUID();
|
|
190
|
+
setDispatchGroupKey(sessionKey, turnGroupKey);
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
replyOptions: {
|
|
194
|
+
onReasoningStream: (payload) => {
|
|
195
|
+
reasoningBuffer += payload.text ?? '';
|
|
196
|
+
},
|
|
197
|
+
onReasoningEnd: async () => {
|
|
198
|
+
if (!reasoningBuffer)
|
|
199
|
+
return;
|
|
200
|
+
stream.push({
|
|
201
|
+
type: 'thinking',
|
|
202
|
+
text: reasoningBuffer,
|
|
203
|
+
groupKey: turnGroupKey || undefined,
|
|
204
|
+
});
|
|
205
|
+
reasoningBuffer = '';
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
run.then(() => stream.end()).catch((err) => stream.fail(err));
|
|
210
|
+
let sessionId;
|
|
211
|
+
try {
|
|
212
|
+
sessionId = await Promise.race([
|
|
213
|
+
waitForOpenClawSessionId(opts.sessionsDir, sessionKey),
|
|
214
|
+
run.then(() => null, (err) => {
|
|
215
|
+
throw err;
|
|
216
|
+
}),
|
|
217
|
+
]);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
sessionId = null;
|
|
221
|
+
}
|
|
222
|
+
if (!sessionId) {
|
|
223
|
+
stream.fail(new Error(`OpenClaw did not expose a sessionId for ${sessionKey}`));
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
yield {
|
|
227
|
+
type: 'runtime_session',
|
|
228
|
+
runtimeSessionId: sessionId,
|
|
229
|
+
runtimeLaneKey: sessionKey,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
yield* stream.stream();
|
|
202
233
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
runtimeSessionId: sessionId,
|
|
207
|
-
runtimeLaneKey: sessionKey,
|
|
208
|
-
};
|
|
234
|
+
finally {
|
|
235
|
+
activeStreams.delete(sessionKey);
|
|
236
|
+
releasePreparedAttachments();
|
|
209
237
|
}
|
|
210
|
-
yield* stream.stream();
|
|
211
238
|
},
|
|
212
239
|
getBranchPoint(sessionKey) {
|
|
213
240
|
const transcriptFile = resolveTranscriptFile(opts.sessionsDir, sessionKey);
|
|
@@ -234,7 +261,7 @@ function createOpenClawDispatchAdapter(opts) {
|
|
|
234
261
|
});
|
|
235
262
|
},
|
|
236
263
|
cleanupFork({ fork }) {
|
|
237
|
-
if (typeof fork.sessionFile !==
|
|
264
|
+
if (typeof fork.sessionFile !== 'string')
|
|
238
265
|
return;
|
|
239
266
|
cleanupForkSession({
|
|
240
267
|
sessionFile: fork.sessionFile,
|
|
@@ -250,7 +277,7 @@ export const parallGateway = {
|
|
|
250
277
|
if (!account.enabled)
|
|
251
278
|
return;
|
|
252
279
|
if (!account.configured)
|
|
253
|
-
throw new Error(
|
|
280
|
+
throw new Error('Parall account is not configured: parall_url/api_key/org_id required');
|
|
254
281
|
const { config } = account;
|
|
255
282
|
const core = getParallRuntime();
|
|
256
283
|
const log = ctx.log;
|
|
@@ -267,106 +294,122 @@ export const parallGateway = {
|
|
|
267
294
|
description: me.agent_profile?.description ?? undefined,
|
|
268
295
|
});
|
|
269
296
|
log?.info(`parall[${ctx.accountId}]: authenticated as ${me.display_name} (${agentUserId})`);
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
const openclawConfigPath = path.join(stateDir, "openclaw.json");
|
|
273
|
-
const configManagerOpts = {
|
|
274
|
-
client,
|
|
275
|
-
stateDir,
|
|
276
|
-
configPath: openclawConfigPath,
|
|
277
|
-
credentials: { api_key: config.api_key, parall_url: config.parall_url },
|
|
278
|
-
log,
|
|
279
|
-
};
|
|
297
|
+
const telemetry = await initAgentTelemetry('parall-openclaw-agent', 'openclaw');
|
|
298
|
+
const otelLog = createOtelLogger('agent', 'openclaw-agent');
|
|
280
299
|
try {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
let stopWikiHelper = null;
|
|
287
|
-
let wikiMountRoot;
|
|
288
|
-
try {
|
|
289
|
-
const wikiHelper = await startWikiHelper({
|
|
290
|
-
accountId: ctx.accountId,
|
|
291
|
-
parallUrl: config.parall_url,
|
|
292
|
-
apiKey: config.api_key,
|
|
293
|
-
orgId: config.org_id,
|
|
294
|
-
agentId: agentUserId,
|
|
300
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR || path.join(process.env.HOME || '/data', '.openclaw');
|
|
301
|
+
const openclawConfigPath = path.join(stateDir, 'openclaw.json');
|
|
302
|
+
const configManagerOpts = {
|
|
303
|
+
client,
|
|
295
304
|
stateDir,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
catch (err) {
|
|
302
|
-
log?.warn(`parall[${ctx.accountId}]: wiki helper startup failed: ${String(err)}`);
|
|
303
|
-
}
|
|
304
|
-
const wsUrl = resolveWsUrl(account);
|
|
305
|
-
const ws = new ParallWs({
|
|
306
|
-
getTicket: () => client.getWsTicket(),
|
|
307
|
-
wsUrl,
|
|
308
|
-
});
|
|
309
|
-
const orchestratorKey = buildOrchestratorSessionKey(ctx.accountId);
|
|
310
|
-
const sessionsDir = path.join(stateDir, "agents", "main", "sessions");
|
|
311
|
-
const dispatchAdapter = createOpenClawDispatchAdapter({
|
|
312
|
-
core,
|
|
313
|
-
cfg: ctx.cfg,
|
|
314
|
-
accountId: ctx.accountId,
|
|
315
|
-
sessionsDir,
|
|
316
|
-
});
|
|
317
|
-
const gateway = new ParallAgentGateway({
|
|
318
|
-
accountId: ctx.accountId,
|
|
319
|
-
client,
|
|
320
|
-
ws,
|
|
321
|
-
connectionLabel: wsUrl,
|
|
322
|
-
config: {
|
|
323
|
-
parall_url: config.parall_url,
|
|
324
|
-
api_key: config.api_key,
|
|
325
|
-
org_id: config.org_id,
|
|
326
|
-
},
|
|
327
|
-
agentUserId,
|
|
328
|
-
runtimeType: "openclaw",
|
|
329
|
-
runtimeKey: orchestratorKey,
|
|
330
|
-
runtimeRef: { hostname: os.hostname(), pid: process.pid },
|
|
331
|
-
dispatchAdapter,
|
|
332
|
-
log,
|
|
333
|
-
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
334
|
-
onConfigUpdate: async () => {
|
|
305
|
+
configPath: openclawConfigPath,
|
|
306
|
+
credentials: { api_key: config.api_key, parall_url: config.parall_url },
|
|
307
|
+
log: otelLog,
|
|
308
|
+
};
|
|
309
|
+
try {
|
|
335
310
|
await fetchAndApplyPlatformConfig(configManagerOpts);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
otelLog.warn(`parall[${ctx.accountId}]: platform config fetch failed: ${String(err)}`);
|
|
314
|
+
}
|
|
315
|
+
let stopWikiHelper = null;
|
|
316
|
+
let wikiMountRoot;
|
|
317
|
+
try {
|
|
318
|
+
const wikiHelper = await startWikiHelper({
|
|
319
|
+
accountId: ctx.accountId,
|
|
320
|
+
parallUrl: config.parall_url,
|
|
341
321
|
apiKey: config.api_key,
|
|
342
322
|
orgId: config.org_id,
|
|
343
|
-
agentUserId,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
sessionsDir,
|
|
347
|
-
runtimeRef: { hostname: os.hostname(), pid: process.pid },
|
|
348
|
-
wikiMountRoot,
|
|
349
|
-
ws,
|
|
350
|
-
orchestratorSessionKey: orchestratorKey,
|
|
323
|
+
agentId: agentUserId,
|
|
324
|
+
stateDir,
|
|
325
|
+
log: otelLog,
|
|
351
326
|
});
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
327
|
+
wikiMountRoot = wikiHelper.mountRoot;
|
|
328
|
+
stopWikiHelper = wikiHelper.stop;
|
|
329
|
+
}
|
|
330
|
+
catch (err) {
|
|
331
|
+
otelLog.warn(`parall[${ctx.accountId}]: wiki helper startup failed: ${String(err)}`);
|
|
332
|
+
}
|
|
333
|
+
const wsUrl = resolveWsUrl(account);
|
|
334
|
+
const ws = new ParallWs({
|
|
335
|
+
getTicket: () => client.getWsTicket(),
|
|
336
|
+
wsUrl,
|
|
337
|
+
});
|
|
338
|
+
const orchestratorKey = buildOrchestratorSessionKey(ctx.accountId);
|
|
339
|
+
const sessionsDir = path.join(stateDir, 'agents', 'main', 'sessions');
|
|
340
|
+
const workspaceDir = process.cwd();
|
|
341
|
+
ensureLocalAttachmentGitExclude(workspaceDir);
|
|
342
|
+
const dispatchAdapter = createOpenClawDispatchAdapter({
|
|
343
|
+
core,
|
|
344
|
+
cfg: ctx.cfg,
|
|
345
|
+
accountId: ctx.accountId,
|
|
346
|
+
sessionsDir,
|
|
347
|
+
workspaceDir,
|
|
348
|
+
});
|
|
349
|
+
const gateway = new ParallAgentGateway({
|
|
350
|
+
accountId: ctx.accountId,
|
|
351
|
+
client,
|
|
352
|
+
ws,
|
|
353
|
+
connectionLabel: wsUrl,
|
|
354
|
+
config: {
|
|
355
|
+
parall_url: config.parall_url,
|
|
356
|
+
api_key: config.api_key,
|
|
357
|
+
org_id: config.org_id,
|
|
358
|
+
},
|
|
359
|
+
agentUserId,
|
|
360
|
+
runtimeType: 'openclaw',
|
|
361
|
+
runtimeKey: orchestratorKey,
|
|
362
|
+
runtimeRef: { hostname: os.hostname(), pid: process.pid },
|
|
363
|
+
dispatchAdapter,
|
|
364
|
+
log: otelLog,
|
|
365
|
+
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
366
|
+
forkDeadlineMs: parseForkDeadlineMs(process.env.PRLL_FORK_DEADLINE_MS),
|
|
367
|
+
dispatchDeadlineMs: parseDispatchDeadlineMs(process.env.PRLL_DISPATCH_DEADLINE_MS),
|
|
368
|
+
onConfigUpdate: async () => {
|
|
369
|
+
await fetchAndApplyPlatformConfig(configManagerOpts);
|
|
370
|
+
},
|
|
371
|
+
onSessionReady: async ({ activeSessionId }) => {
|
|
372
|
+
setParallAccountState(ctx.accountId, {
|
|
373
|
+
client,
|
|
374
|
+
apiUrl: config.parall_url,
|
|
375
|
+
apiKey: config.api_key,
|
|
376
|
+
orgId: config.org_id,
|
|
377
|
+
agentUserId,
|
|
378
|
+
activeSessionId,
|
|
379
|
+
sessionBindings: new Map(),
|
|
380
|
+
sessionsDir,
|
|
381
|
+
runtimeRef: { hostname: os.hostname(), pid: process.pid },
|
|
382
|
+
wikiMountRoot,
|
|
383
|
+
ws,
|
|
384
|
+
orchestratorSessionKey: orchestratorKey,
|
|
385
|
+
});
|
|
386
|
+
},
|
|
387
|
+
onSessionBinding: async ({ sessionKey, agentSessionId }) => {
|
|
388
|
+
setAgentSessionBinding(ctx.accountId, sessionKey, agentSessionId);
|
|
389
|
+
},
|
|
390
|
+
onBeforeDisconnect: async () => {
|
|
391
|
+
stopWikiHelper?.();
|
|
392
|
+
removeParallAccountState(ctx.accountId);
|
|
393
|
+
},
|
|
394
|
+
onNewSession: () => {
|
|
395
|
+
clearSessionState(sessionsDir, orchestratorKey);
|
|
396
|
+
},
|
|
397
|
+
onSessionStale: () => {
|
|
398
|
+
clearSessionState(sessionsDir, orchestratorKey);
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
try {
|
|
402
|
+
await gateway.run(ctx.abortSignal);
|
|
403
|
+
}
|
|
404
|
+
finally {
|
|
405
|
+
if (!ctx.abortSignal.aborted) {
|
|
406
|
+
stopWikiHelper?.();
|
|
407
|
+
removeParallAccountState(ctx.accountId);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
364
410
|
}
|
|
365
411
|
finally {
|
|
366
|
-
|
|
367
|
-
stopWikiHelper?.();
|
|
368
|
-
removeParallAccountState(ctx.accountId);
|
|
369
|
-
}
|
|
412
|
+
await telemetry.shutdown();
|
|
370
413
|
}
|
|
371
414
|
},
|
|
372
415
|
};
|
package/dist/hooks.d.ts
CHANGED
package/dist/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AA+G7D,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,iBAAiB,QA6KzD"}
|
package/dist/hooks.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { PRLL_BEHAVIOR, PRLL_REFERENCE_GUIDE, buildIdentity } from
|
|
2
|
-
import { extractAccountIdFromSessionKey } from
|
|
3
|
-
import { clearDispatchGroupKey, getAgentIdentity, getAgentSessionBinding, getDispatchGroupKey, getDispatchMessageId, getParallAccountState, getSessionChatId, setAgentSessionBinding } from
|
|
4
|
-
import { resolveSessionId } from
|
|
1
|
+
import { PRLL_BEHAVIOR, PRLL_REFERENCE_GUIDE, buildIdentity, isParallSendCommand, isParallNoReplyCommand, recordToolCall, recordMessageSend, recordNoReply, } from '@parall/agent-core';
|
|
2
|
+
import { extractAccountIdFromSessionKey } from './session.js';
|
|
3
|
+
import { clearDispatchGroupKey, getAgentIdentity, getAgentSessionBinding, getDispatchGroupKey, getDispatchMessageId, getParallAccountState, getSessionChatId, setAgentSessionBinding, } from './runtime.js';
|
|
4
|
+
import { resolveSessionId } from './fork.js';
|
|
5
5
|
/**
|
|
6
6
|
* Map the session's stored target ID (whatever the current dispatch routed
|
|
7
7
|
* to — chat, task, or schedule) to the `target_type` field on agent_step.
|
|
8
8
|
* Keeps tool_call / tool_result attribution correct across event types.
|
|
9
9
|
*/
|
|
10
10
|
function stepTargetType(targetId) {
|
|
11
|
-
if (targetId.startsWith(
|
|
12
|
-
return
|
|
13
|
-
if (targetId.startsWith(
|
|
14
|
-
return
|
|
15
|
-
if (targetId.startsWith(
|
|
16
|
-
return
|
|
17
|
-
return
|
|
11
|
+
if (targetId.startsWith('cht_'))
|
|
12
|
+
return 'chat';
|
|
13
|
+
if (targetId.startsWith('tsk_'))
|
|
14
|
+
return 'task';
|
|
15
|
+
if (targetId.startsWith('sch_'))
|
|
16
|
+
return 'schedule';
|
|
17
|
+
return '';
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Resolve the ParallClient + orgId + sessionId for a hook context.
|
|
@@ -46,7 +46,7 @@ async function resolveClientForHook(sessionKey) {
|
|
|
46
46
|
: undefined;
|
|
47
47
|
try {
|
|
48
48
|
const session = await state.client.createAgentSession(state.orgId, state.agentUserId, {
|
|
49
|
-
runtime_type:
|
|
49
|
+
runtime_type: 'openclaw',
|
|
50
50
|
runtime_key: sessionKey,
|
|
51
51
|
runtime_lane_key: sessionKey,
|
|
52
52
|
runtime_session_id: runtimeSessionId,
|
|
@@ -100,14 +100,34 @@ export function registerParallHooks(api) {
|
|
|
100
100
|
// registration time, only after the gateway authenticates via getMe().
|
|
101
101
|
// Uses process-global identity because hosted agents run one-agent-per-pod;
|
|
102
102
|
// before_prompt_build doesn't receive session context anyway.
|
|
103
|
-
api.on(
|
|
103
|
+
api.on('before_prompt_build', () => {
|
|
104
104
|
return {
|
|
105
|
-
appendSystemContext: [
|
|
105
|
+
appendSystemContext: [
|
|
106
|
+
buildIdentity(getAgentIdentity()),
|
|
107
|
+
PRLL_CHANNEL_CONTEXT,
|
|
108
|
+
PRLL_BEHAVIOR,
|
|
109
|
+
PRLL_REFERENCE_GUIDE,
|
|
110
|
+
].join('\n\n'),
|
|
106
111
|
};
|
|
107
112
|
});
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
const pendingSendCalls = new Map();
|
|
114
|
+
// before_tool_call -> (1) update dispatch metrics, (2) await step creation, (3) ENV injection
|
|
115
|
+
// OpenClaw tool calls bypass the RuntimeEvent stream (they go through hooks, not the
|
|
116
|
+
// gateway-base for-await loop), so dispatch metrics must be updated here as well.
|
|
117
|
+
api.on('before_tool_call', async (event, ctx) => {
|
|
110
118
|
const sessionKey = ctx.sessionKey;
|
|
119
|
+
if (sessionKey) {
|
|
120
|
+
recordToolCall(sessionKey);
|
|
121
|
+
if (event.toolName === 'exec' && event.toolCallId) {
|
|
122
|
+
const command = event.params?.command;
|
|
123
|
+
if (isParallSendCommand(command)) {
|
|
124
|
+
pendingSendCalls.set(event.toolCallId, true);
|
|
125
|
+
}
|
|
126
|
+
else if (isParallNoReplyCommand(command)) {
|
|
127
|
+
recordNoReply(sessionKey);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
111
131
|
// (1) Create tool_call step and await to get step ID
|
|
112
132
|
let stepId;
|
|
113
133
|
const resolved = await resolveClientForHook(sessionKey);
|
|
@@ -115,14 +135,14 @@ export function registerParallHooks(api) {
|
|
|
115
135
|
const groupKey = sessionKey ? getDispatchGroupKey(sessionKey) : undefined;
|
|
116
136
|
try {
|
|
117
137
|
const step = await resolved.client.createAgentStep(resolved.orgId, resolved.agentUserId, resolved.sessionId, {
|
|
118
|
-
step_type:
|
|
138
|
+
step_type: 'tool_call',
|
|
119
139
|
target_type: stepTargetType(resolved.chatId),
|
|
120
140
|
target_id: resolved.chatId || undefined,
|
|
121
141
|
content: {
|
|
122
142
|
call_id: event.toolCallId,
|
|
123
143
|
tool_name: event.toolName,
|
|
124
144
|
tool_input: event.params ?? {},
|
|
125
|
-
status:
|
|
145
|
+
status: 'running',
|
|
126
146
|
started_at: new Date().toISOString(),
|
|
127
147
|
},
|
|
128
148
|
group_key: groupKey,
|
|
@@ -135,7 +155,7 @@ export function registerParallHooks(api) {
|
|
|
135
155
|
}
|
|
136
156
|
}
|
|
137
157
|
// (2) ENV injection — only for the exec (Bash) tool
|
|
138
|
-
if (event.toolName !==
|
|
158
|
+
if (event.toolName !== 'exec')
|
|
139
159
|
return;
|
|
140
160
|
if (!sessionKey)
|
|
141
161
|
return;
|
|
@@ -162,7 +182,7 @@ export function registerParallHooks(api) {
|
|
|
162
182
|
if (stepId)
|
|
163
183
|
injectedEnv.PRLL_STEP_ID = stepId;
|
|
164
184
|
if (chatId) {
|
|
165
|
-
if (chatId.startsWith(
|
|
185
|
+
if (chatId.startsWith('sch_')) {
|
|
166
186
|
// Schedule-triggered dispatch: expose PRLL_SCHEDULE_ID so the agent
|
|
167
187
|
// can resolve the schedule; deliberately do NOT set PRLL_CHAT_ID,
|
|
168
188
|
// because schedule events have no chat to reply into, and leaving
|
|
@@ -194,21 +214,27 @@ export function registerParallHooks(api) {
|
|
|
194
214
|
},
|
|
195
215
|
};
|
|
196
216
|
});
|
|
197
|
-
// after_tool_call -> send tool_result step to AgentSession
|
|
198
|
-
api.on(
|
|
217
|
+
// after_tool_call -> track CLI outcomes + send tool_result step to AgentSession
|
|
218
|
+
api.on('after_tool_call', async (event, ctx) => {
|
|
219
|
+
if (ctx.sessionKey && event.toolCallId && pendingSendCalls.delete(event.toolCallId)) {
|
|
220
|
+
recordMessageSend(ctx.sessionKey, !event.error);
|
|
221
|
+
}
|
|
199
222
|
const resolved = await resolveClientForHook(ctx.sessionKey);
|
|
200
223
|
if (!resolved?.sessionId || !event.toolCallId)
|
|
201
224
|
return;
|
|
202
225
|
try {
|
|
203
226
|
await resolved.client.createAgentStep(resolved.orgId, resolved.agentUserId, resolved.sessionId, {
|
|
204
|
-
step_type:
|
|
227
|
+
step_type: 'tool_result',
|
|
205
228
|
target_type: stepTargetType(resolved.chatId),
|
|
206
229
|
target_id: resolved.chatId || undefined,
|
|
207
230
|
content: {
|
|
208
231
|
call_id: event.toolCallId,
|
|
209
232
|
tool_name: event.toolName,
|
|
210
|
-
status: event.error ?
|
|
211
|
-
output: event.error ??
|
|
233
|
+
status: event.error ? 'error' : 'success',
|
|
234
|
+
output: event.error ??
|
|
235
|
+
(typeof event.result === 'string'
|
|
236
|
+
? event.result
|
|
237
|
+
: JSON.stringify(event.result ?? '')),
|
|
212
238
|
duration_ms: event.durationMs ?? 0,
|
|
213
239
|
collapsible: true,
|
|
214
240
|
},
|
|
@@ -221,7 +247,7 @@ export function registerParallHooks(api) {
|
|
|
221
247
|
// agent_end -> end of a dispatch cycle, NOT end of session.
|
|
222
248
|
// Session lives across dispatches — don't update session status here.
|
|
223
249
|
// Dispatch-specific IDs are cleaned up in the generic gateway; hooks only own the group key.
|
|
224
|
-
api.on(
|
|
250
|
+
api.on('agent_end', async (event, ctx) => {
|
|
225
251
|
if (ctx.sessionKey) {
|
|
226
252
|
clearDispatchGroupKey(ctx.sessionKey);
|
|
227
253
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { emptyPluginConfigSchema } from
|
|
2
|
-
import { parallPlugin } from
|
|
3
|
-
import { setParallRuntime } from
|
|
4
|
-
import { registerParallHooks } from
|
|
1
|
+
import { emptyPluginConfigSchema } from 'openclaw/plugin-sdk/core';
|
|
2
|
+
import { parallPlugin } from './channel.js';
|
|
3
|
+
import { setParallRuntime } from './runtime.js';
|
|
4
|
+
import { registerParallHooks } from './hooks.js';
|
|
5
5
|
const plugin = {
|
|
6
|
-
id:
|
|
7
|
-
name:
|
|
8
|
-
description:
|
|
6
|
+
id: 'parall',
|
|
7
|
+
name: 'Parall',
|
|
8
|
+
description: 'Parall IM channel plugin — Agent-Native messaging with tool call visualization.',
|
|
9
9
|
configSchema: emptyPluginConfigSchema(),
|
|
10
10
|
register(api) {
|
|
11
11
|
setParallRuntime(api.runtime);
|
package/dist/oc-session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oc-session.d.ts","sourceRoot":"","sources":["../src/oc-session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oc-session.d.ts","sourceRoot":"","sources":["../src/oc-session.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAsHzC,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,IAAI,CAAmC;IAC/C,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,mBAAmB,CAA6B;IACxD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO;IAeP,OAAO,CAAC,IAAI;IA+BZ,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,UAAU;IAsBlB,OAAO,CAAC,OAAO;IAUf,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B,MAAM,IAAI,MAAM;IAIhB,aAAa,IAAI,MAAM;IAIvB,YAAY,IAAI,MAAM;IAItB,cAAc,IAAI,MAAM,GAAG,SAAS;IAMpC,OAAO,CAAC,SAAS;IAWjB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IA+DzD,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;CAO1C"}
|