@oyasmi/pipiclaw 0.6.5 → 0.6.6-beta.1
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.
|
@@ -24,6 +24,7 @@ export declare class ChannelRunner implements AgentRunner {
|
|
|
24
24
|
private activeModel;
|
|
25
25
|
private currentSkills;
|
|
26
26
|
private firstTurnMemoryBootstrapPending;
|
|
27
|
+
private acceptingBusyMessages;
|
|
27
28
|
private runState;
|
|
28
29
|
constructor(sandboxConfig: SandboxConfig, channelId: string, channelDir: string);
|
|
29
30
|
run(ctx: DingTalkContext, store: ChannelStore): Promise<{
|
|
@@ -53,6 +53,7 @@ function asSdkSettingsManager(manager) {
|
|
|
53
53
|
export class ChannelRunner {
|
|
54
54
|
constructor(sandboxConfig, channelId, channelDir) {
|
|
55
55
|
this.firstTurnMemoryBootstrapPending = true;
|
|
56
|
+
this.acceptingBusyMessages = false;
|
|
56
57
|
// --- Per run ---
|
|
57
58
|
this.runState = createEmptyRunState();
|
|
58
59
|
this.sandboxConfig = sandboxConfig;
|
|
@@ -169,8 +170,10 @@ export class ChannelRunner {
|
|
|
169
170
|
// === Public API ===
|
|
170
171
|
async run(ctx, store) {
|
|
171
172
|
this.resetRunState(ctx, store);
|
|
173
|
+
this.acceptingBusyMessages = true;
|
|
172
174
|
const runQueue = createRunQueue(ctx);
|
|
173
175
|
this.runState.queue = runQueue.queue;
|
|
176
|
+
let promptSubmitted = false;
|
|
174
177
|
try {
|
|
175
178
|
await this.ensureSessionReady();
|
|
176
179
|
this.memoryLifecycle.noteUserTurnStarted();
|
|
@@ -225,6 +228,7 @@ export class ChannelRunner {
|
|
|
225
228
|
}
|
|
226
229
|
await this.sessionResourceGate.runPrompt(async () => {
|
|
227
230
|
await this.session.prompt(promptText);
|
|
231
|
+
promptSubmitted = true;
|
|
228
232
|
});
|
|
229
233
|
}
|
|
230
234
|
catch (err) {
|
|
@@ -233,6 +237,14 @@ export class ChannelRunner {
|
|
|
233
237
|
log.logWarning(`[${this.channelId}] Runner failed`, this.runState.errorMessage);
|
|
234
238
|
}
|
|
235
239
|
finally {
|
|
240
|
+
this.acceptingBusyMessages = false;
|
|
241
|
+
if (!promptSubmitted) {
|
|
242
|
+
const discarded = this.session.clearQueue();
|
|
243
|
+
const discardedCount = discarded.steering.length + discarded.followUp.length;
|
|
244
|
+
if (discardedCount > 0) {
|
|
245
|
+
log.logWarning(`[${this.channelId}] Discarded ${discardedCount} queued busy message(s) after run setup failed`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
236
248
|
await runQueue.drain();
|
|
237
249
|
const finalOutcome = this.runState.finalOutcome;
|
|
238
250
|
const finalOutcomeText = getFinalOutcomeText(finalOutcome);
|
|
@@ -423,20 +435,28 @@ export class ChannelRunner {
|
|
|
423
435
|
return `[${timestamp}] [${userName || "unknown"}]: ${text}`;
|
|
424
436
|
}
|
|
425
437
|
async queueBusyMessage(delivery, text, userName) {
|
|
426
|
-
if (!this.
|
|
438
|
+
if (!this.acceptingBusyMessages) {
|
|
427
439
|
throw new Error("No task is currently running.");
|
|
428
440
|
}
|
|
441
|
+
await this.ensureSessionReady();
|
|
429
442
|
const clippedText = clipUserInput(text, MAX_USER_MESSAGE_CHARS);
|
|
430
443
|
if (clippedText !== text.trim()) {
|
|
431
444
|
log.logWarning(`[${this.channelId}] Queued message exceeded ${MAX_USER_MESSAGE_CHARS} chars and was clipped`);
|
|
432
445
|
}
|
|
433
446
|
const queuedMessage = this.formatUserMessage(clippedText, userName);
|
|
434
447
|
await this.maybeRunPreventiveCompactionForIncomingText(queuedMessage);
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
448
|
+
if (!this.acceptingBusyMessages) {
|
|
449
|
+
throw new Error("No task is currently running.");
|
|
450
|
+
}
|
|
451
|
+
const queueMessage = async () => {
|
|
452
|
+
if (delivery === "followUp") {
|
|
453
|
+
await this.session.followUp(queuedMessage);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
await this.session.steer(queuedMessage);
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
await this.sessionResourceGate.runPrompt(queueMessage);
|
|
440
460
|
}
|
|
441
461
|
resetRunState(ctx, store) {
|
|
442
462
|
this.runState = createEmptyRunState();
|
|
@@ -341,6 +341,9 @@ function flushInactiveChannelMemory(channelStates) {
|
|
|
341
341
|
}
|
|
342
342
|
return flushes;
|
|
343
343
|
}
|
|
344
|
+
function isNoRunningTaskQueueError(err) {
|
|
345
|
+
return err instanceof Error && err.message === "No task is currently running.";
|
|
346
|
+
}
|
|
344
347
|
export function createRuntimeContext(options) {
|
|
345
348
|
const startServices = options.startServices ?? true;
|
|
346
349
|
const registerSignalHandlers = options.registerSignalHandlers ?? true;
|
|
@@ -390,20 +393,10 @@ export function createRuntimeContext(options) {
|
|
|
390
393
|
},
|
|
391
394
|
async handleBusyMessage(event, bot, mode, queueText) {
|
|
392
395
|
if (shuttingDown) {
|
|
393
|
-
return;
|
|
396
|
+
return true;
|
|
394
397
|
}
|
|
395
398
|
const state = getState(event.channelId);
|
|
396
399
|
const trimmedQueueText = queueText.trim();
|
|
397
|
-
await archiveIncomingMessage(event.channelId, {
|
|
398
|
-
date: new Date().toISOString(),
|
|
399
|
-
ts: event.ts,
|
|
400
|
-
user: event.user,
|
|
401
|
-
userName: event.userName,
|
|
402
|
-
text: event.text,
|
|
403
|
-
isBot: false,
|
|
404
|
-
deliveryMode: mode,
|
|
405
|
-
skipContextSync: true,
|
|
406
|
-
}, `${mode} message`);
|
|
407
400
|
try {
|
|
408
401
|
if (mode === "followUp") {
|
|
409
402
|
await state.runner.queueFollowUp(trimmedQueueText, event.userName);
|
|
@@ -411,6 +404,16 @@ export function createRuntimeContext(options) {
|
|
|
411
404
|
else {
|
|
412
405
|
await state.runner.queueSteer(trimmedQueueText, event.userName);
|
|
413
406
|
}
|
|
407
|
+
await archiveIncomingMessage(event.channelId, {
|
|
408
|
+
date: new Date().toISOString(),
|
|
409
|
+
ts: event.ts,
|
|
410
|
+
user: event.user,
|
|
411
|
+
userName: event.userName,
|
|
412
|
+
text: event.text,
|
|
413
|
+
isBot: false,
|
|
414
|
+
deliveryMode: mode,
|
|
415
|
+
skipContextSync: true,
|
|
416
|
+
}, `${mode} message`);
|
|
414
417
|
const confirmation = mode === "followUp"
|
|
415
418
|
? event.text.trim().startsWith("/")
|
|
416
419
|
? "Queued as follow-up. I’ll handle it after the current task completes."
|
|
@@ -420,11 +423,17 @@ export function createRuntimeContext(options) {
|
|
|
420
423
|
: "Queued as steer. I’ll apply this after the current tool step finishes. Use `/followup <message>` to queue it after completion.";
|
|
421
424
|
await bot.sendPlain(event.channelId, confirmation);
|
|
422
425
|
log.logInfo(`[${event.channelId}] Queued ${mode}: ${trimmedQueueText.substring(0, 80)}`);
|
|
426
|
+
return true;
|
|
423
427
|
}
|
|
424
428
|
catch (err) {
|
|
425
429
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
430
|
+
if (isNoRunningTaskQueueError(err)) {
|
|
431
|
+
log.logInfo(`[${event.channelId}] Busy ${mode} window closed; requeueing as a normal message`);
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
426
434
|
log.logWarning(`[${event.channelId}] Failed to queue ${mode}`, errMsg);
|
|
427
435
|
await bot.sendPlain(event.channelId, `Could not queue this message: ${errMsg}`);
|
|
436
|
+
return true;
|
|
428
437
|
}
|
|
429
438
|
},
|
|
430
439
|
async handleEvent(event, bot, _isEvent) {
|
|
@@ -51,7 +51,7 @@ export interface DingTalkHandler {
|
|
|
51
51
|
isRunning(channelId: string): boolean;
|
|
52
52
|
handleEvent(event: DingTalkEvent, bot: DingTalkBot, isEvent?: boolean): Promise<void>;
|
|
53
53
|
handleStop(channelId: string, bot: DingTalkBot): Promise<void>;
|
|
54
|
-
handleBusyMessage(event: DingTalkEvent, bot: DingTalkBot, mode: BusyMessageMode, queueText: string): Promise<
|
|
54
|
+
handleBusyMessage(event: DingTalkEvent, bot: DingTalkBot, mode: BusyMessageMode, queueText: string): Promise<boolean>;
|
|
55
55
|
}
|
|
56
56
|
export declare class DingTalkBot {
|
|
57
57
|
private handler;
|
|
@@ -105,6 +105,7 @@ export declare class DingTalkBot {
|
|
|
105
105
|
* Returns true if enqueued, false if queue is full (max 5).
|
|
106
106
|
*/
|
|
107
107
|
enqueueEvent(event: DingTalkEvent): boolean;
|
|
108
|
+
private enqueueStreamMessage;
|
|
108
109
|
/**
|
|
109
110
|
* Get or create an AI Card for a channel.
|
|
110
111
|
*/
|
package/dist/runtime/dingtalk.js
CHANGED
|
@@ -481,6 +481,18 @@ export class DingTalkBot {
|
|
|
481
481
|
});
|
|
482
482
|
return true;
|
|
483
483
|
}
|
|
484
|
+
enqueueStreamMessage(event) {
|
|
485
|
+
this.getQueue(event.channelId).enqueue(async () => {
|
|
486
|
+
this.activeMessageProcessing = true;
|
|
487
|
+
try {
|
|
488
|
+
await this.handler.handleEvent(event, this);
|
|
489
|
+
}
|
|
490
|
+
finally {
|
|
491
|
+
this.activeMessageProcessing = false;
|
|
492
|
+
this.lastSocketAvailableTime = Date.now();
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
}
|
|
484
496
|
// ==========================================================================
|
|
485
497
|
// AI Card operations
|
|
486
498
|
// ==========================================================================
|
|
@@ -858,11 +870,17 @@ export class DingTalkBot {
|
|
|
858
870
|
return;
|
|
859
871
|
}
|
|
860
872
|
if (builtInCommand?.name === "steer") {
|
|
861
|
-
await this.handler.handleBusyMessage(event, this, "steer", builtInCommand.args);
|
|
873
|
+
const handled = await this.handler.handleBusyMessage(event, this, "steer", builtInCommand.args);
|
|
874
|
+
if (!handled) {
|
|
875
|
+
this.enqueueStreamMessage(event);
|
|
876
|
+
}
|
|
862
877
|
return;
|
|
863
878
|
}
|
|
864
879
|
if (builtInCommand?.name === "followup") {
|
|
865
|
-
await this.handler.handleBusyMessage(event, this, "followUp", builtInCommand.args);
|
|
880
|
+
const handled = await this.handler.handleBusyMessage(event, this, "followUp", builtInCommand.args);
|
|
881
|
+
if (!handled) {
|
|
882
|
+
this.enqueueStreamMessage(event);
|
|
883
|
+
}
|
|
866
884
|
return;
|
|
867
885
|
}
|
|
868
886
|
if (builtInCommand) {
|
|
@@ -873,20 +891,14 @@ export class DingTalkBot {
|
|
|
873
891
|
await this.sendPlain(channelId, "A task is already running. Only `/stop`, `/steer <message>`, and `/followup <message>` are available while streaming.");
|
|
874
892
|
return;
|
|
875
893
|
}
|
|
876
|
-
await this.handler.handleBusyMessage(event, this, this.busyMessageDefault, content);
|
|
894
|
+
const handled = await this.handler.handleBusyMessage(event, this, this.busyMessageDefault, content);
|
|
895
|
+
if (!handled) {
|
|
896
|
+
this.enqueueStreamMessage(event);
|
|
897
|
+
}
|
|
877
898
|
return;
|
|
878
899
|
}
|
|
879
900
|
// Enqueue for processing
|
|
880
|
-
this.
|
|
881
|
-
this.activeMessageProcessing = true;
|
|
882
|
-
try {
|
|
883
|
-
await this.handler.handleEvent(event, this);
|
|
884
|
-
}
|
|
885
|
-
finally {
|
|
886
|
-
this.activeMessageProcessing = false;
|
|
887
|
-
this.lastSocketAvailableTime = Date.now();
|
|
888
|
-
}
|
|
889
|
-
});
|
|
901
|
+
this.enqueueStreamMessage(event);
|
|
890
902
|
}
|
|
891
903
|
getQueue(channelId) {
|
|
892
904
|
let queue = this.queues.get(channelId);
|
package/package.json
CHANGED