@oyasmi/pipiclaw 0.6.6-beta.1 → 0.6.6-beta.3
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.
|
@@ -25,6 +25,7 @@ export declare class ChannelRunner implements AgentRunner {
|
|
|
25
25
|
private currentSkills;
|
|
26
26
|
private firstTurnMemoryBootstrapPending;
|
|
27
27
|
private acceptingBusyMessages;
|
|
28
|
+
private agentLoopStarted;
|
|
28
29
|
private runState;
|
|
29
30
|
constructor(sandboxConfig: SandboxConfig, channelId: string, channelDir: string);
|
|
30
31
|
run(ctx: DingTalkContext, store: ChannelStore): Promise<{
|
|
@@ -33,7 +34,6 @@ export declare class ChannelRunner implements AgentRunner {
|
|
|
33
34
|
}>;
|
|
34
35
|
handleBuiltinCommand(ctx: DingTalkContext, command: BuiltInCommand): Promise<void>;
|
|
35
36
|
queueSteer(text: string, userName?: string): Promise<void>;
|
|
36
|
-
queueFollowUp(text: string, userName?: string): Promise<void>;
|
|
37
37
|
flushMemoryForShutdown(): Promise<void>;
|
|
38
38
|
getMemoryMaintenanceContext(): Promise<MemoryMaintenanceRuntimeContext>;
|
|
39
39
|
abort(): Promise<void>;
|
|
@@ -54,6 +54,7 @@ export class ChannelRunner {
|
|
|
54
54
|
constructor(sandboxConfig, channelId, channelDir) {
|
|
55
55
|
this.firstTurnMemoryBootstrapPending = true;
|
|
56
56
|
this.acceptingBusyMessages = false;
|
|
57
|
+
this.agentLoopStarted = false;
|
|
57
58
|
// --- Per run ---
|
|
58
59
|
this.runState = createEmptyRunState();
|
|
59
60
|
this.sandboxConfig = sandboxConfig;
|
|
@@ -171,6 +172,7 @@ export class ChannelRunner {
|
|
|
171
172
|
async run(ctx, store) {
|
|
172
173
|
this.resetRunState(ctx, store);
|
|
173
174
|
this.acceptingBusyMessages = true;
|
|
175
|
+
this.agentLoopStarted = false;
|
|
174
176
|
const runQueue = createRunQueue(ctx);
|
|
175
177
|
this.runState.queue = runQueue.queue;
|
|
176
178
|
let promptSubmitted = false;
|
|
@@ -238,6 +240,7 @@ export class ChannelRunner {
|
|
|
238
240
|
}
|
|
239
241
|
finally {
|
|
240
242
|
this.acceptingBusyMessages = false;
|
|
243
|
+
this.agentLoopStarted = false;
|
|
241
244
|
if (!promptSubmitted) {
|
|
242
245
|
const discarded = this.session.clearQueue();
|
|
243
246
|
const discardedCount = discarded.steering.length + discarded.followUp.length;
|
|
@@ -353,10 +356,7 @@ export class ChannelRunner {
|
|
|
353
356
|
}
|
|
354
357
|
}
|
|
355
358
|
async queueSteer(text, userName) {
|
|
356
|
-
await this.queueBusyMessage(
|
|
357
|
-
}
|
|
358
|
-
async queueFollowUp(text, userName) {
|
|
359
|
-
await this.queueBusyMessage("followUp", this.requireQueuedMessage(text, "followup"), userName);
|
|
359
|
+
await this.queueBusyMessage(this.requireQueuedMessage(text, "steer"), userName);
|
|
360
360
|
}
|
|
361
361
|
async flushMemoryForShutdown() {
|
|
362
362
|
await this.memoryLifecycle.flushForShutdown();
|
|
@@ -434,10 +434,13 @@ export class ChannelRunner {
|
|
|
434
434
|
const timestamp = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}${offsetSign}${offsetHours}:${offsetMins}`;
|
|
435
435
|
return `[${timestamp}] [${userName || "unknown"}]: ${text}`;
|
|
436
436
|
}
|
|
437
|
-
async queueBusyMessage(
|
|
437
|
+
async queueBusyMessage(text, userName) {
|
|
438
438
|
if (!this.acceptingBusyMessages) {
|
|
439
439
|
throw new Error("No task is currently running.");
|
|
440
440
|
}
|
|
441
|
+
if (this.agentLoopStarted && !this.session.isStreaming) {
|
|
442
|
+
throw new Error("No task is currently running.");
|
|
443
|
+
}
|
|
441
444
|
await this.ensureSessionReady();
|
|
442
445
|
const clippedText = clipUserInput(text, MAX_USER_MESSAGE_CHARS);
|
|
443
446
|
if (clippedText !== text.trim()) {
|
|
@@ -448,13 +451,14 @@ export class ChannelRunner {
|
|
|
448
451
|
if (!this.acceptingBusyMessages) {
|
|
449
452
|
throw new Error("No task is currently running.");
|
|
450
453
|
}
|
|
454
|
+
if (this.agentLoopStarted && !this.session.isStreaming) {
|
|
455
|
+
throw new Error("No task is currently running.");
|
|
456
|
+
}
|
|
451
457
|
const queueMessage = async () => {
|
|
452
|
-
if (
|
|
453
|
-
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
await this.session.steer(queuedMessage);
|
|
458
|
+
if (this.agentLoopStarted && !this.session.isStreaming) {
|
|
459
|
+
throw new Error("No task is currently running.");
|
|
457
460
|
}
|
|
461
|
+
await this.session.steer(queuedMessage);
|
|
458
462
|
};
|
|
459
463
|
await this.sessionResourceGate.runPrompt(queueMessage);
|
|
460
464
|
}
|
|
@@ -584,6 +588,9 @@ export class ChannelRunner {
|
|
|
584
588
|
// === Session event subscription ===
|
|
585
589
|
subscribeToSessionEvents() {
|
|
586
590
|
this.session.subscribe(async (event) => {
|
|
591
|
+
if (isRecord(event) && event.type === "message_start") {
|
|
592
|
+
this.agentLoopStarted = true;
|
|
593
|
+
}
|
|
587
594
|
if (isRecord(event) && "reason" in event && event.reason === "new") {
|
|
588
595
|
this.firstTurnMemoryBootstrapPending = true;
|
|
589
596
|
}
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ export interface AgentRunner {
|
|
|
10
10
|
}>;
|
|
11
11
|
handleBuiltinCommand(ctx: DingTalkContext, command: BuiltInCommand): Promise<void>;
|
|
12
12
|
queueSteer(text: string, userName?: string): Promise<void>;
|
|
13
|
-
queueFollowUp(text: string, userName?: string): Promise<void>;
|
|
14
13
|
flushMemoryForShutdown(): Promise<void>;
|
|
15
14
|
getMemoryMaintenanceContext(): Promise<MemoryMaintenanceRuntimeContext>;
|
|
16
15
|
abort(): Promise<void>;
|
|
@@ -393,17 +393,21 @@ export function createRuntimeContext(options) {
|
|
|
393
393
|
},
|
|
394
394
|
async handleBusyMessage(event, bot, mode, queueText) {
|
|
395
395
|
if (shuttingDown) {
|
|
396
|
-
return
|
|
396
|
+
return { kind: "handled" };
|
|
397
397
|
}
|
|
398
398
|
const state = getState(event.channelId);
|
|
399
399
|
const trimmedQueueText = queueText.trim();
|
|
400
|
+
if (!trimmedQueueText) {
|
|
401
|
+
const commandName = mode === "followUp" ? "followup" : "steer";
|
|
402
|
+
await bot.sendPlain(event.channelId, `Could not queue this message: /${commandName} requires a message.`);
|
|
403
|
+
return { kind: "handled" };
|
|
404
|
+
}
|
|
405
|
+
if (mode === "followUp") {
|
|
406
|
+
log.logInfo(`[${event.channelId}] Queued followUp as next task: ${trimmedQueueText.substring(0, 80)}`);
|
|
407
|
+
return { kind: "requeue", text: trimmedQueueText };
|
|
408
|
+
}
|
|
400
409
|
try {
|
|
401
|
-
|
|
402
|
-
await state.runner.queueFollowUp(trimmedQueueText, event.userName);
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
await state.runner.queueSteer(trimmedQueueText, event.userName);
|
|
406
|
-
}
|
|
410
|
+
await state.runner.queueSteer(trimmedQueueText, event.userName);
|
|
407
411
|
await archiveIncomingMessage(event.channelId, {
|
|
408
412
|
date: new Date().toISOString(),
|
|
409
413
|
ts: event.ts,
|
|
@@ -414,26 +418,22 @@ export function createRuntimeContext(options) {
|
|
|
414
418
|
deliveryMode: mode,
|
|
415
419
|
skipContextSync: true,
|
|
416
420
|
}, `${mode} message`);
|
|
417
|
-
const confirmation =
|
|
418
|
-
?
|
|
419
|
-
|
|
420
|
-
: "Queued as follow-up. I’ll handle it after the current task completes. Use `/steer <message>` to apply it after the current tool step finishes."
|
|
421
|
-
: event.text.trim().startsWith("/")
|
|
422
|
-
? "Queued as steer. I’ll apply it after the current tool step finishes."
|
|
423
|
-
: "Queued as steer. I’ll apply this after the current tool step finishes. Use `/followup <message>` to queue it after completion.";
|
|
421
|
+
const confirmation = event.text.trim().startsWith("/")
|
|
422
|
+
? "Queued as steer. I’ll apply it after the current tool step finishes."
|
|
423
|
+
: "Queued as steer. I’ll apply this after the current tool step finishes. Use `/followup <message>` to queue it after completion.";
|
|
424
424
|
await bot.sendPlain(event.channelId, confirmation);
|
|
425
425
|
log.logInfo(`[${event.channelId}] Queued ${mode}: ${trimmedQueueText.substring(0, 80)}`);
|
|
426
|
-
return
|
|
426
|
+
return { kind: "handled" };
|
|
427
427
|
}
|
|
428
428
|
catch (err) {
|
|
429
429
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
430
430
|
if (isNoRunningTaskQueueError(err)) {
|
|
431
431
|
log.logInfo(`[${event.channelId}] Busy ${mode} window closed; requeueing as a normal message`);
|
|
432
|
-
return
|
|
432
|
+
return { kind: "requeue", text: trimmedQueueText };
|
|
433
433
|
}
|
|
434
434
|
log.logWarning(`[${event.channelId}] Failed to queue ${mode}`, errMsg);
|
|
435
435
|
await bot.sendPlain(event.channelId, `Could not queue this message: ${errMsg}`);
|
|
436
|
-
return
|
|
436
|
+
return { kind: "handled" };
|
|
437
437
|
}
|
|
438
438
|
},
|
|
439
439
|
async handleEvent(event, bot, _isEvent) {
|
|
@@ -47,11 +47,17 @@ export interface DingTalkContext {
|
|
|
47
47
|
flush: () => Promise<void>;
|
|
48
48
|
close: () => Promise<void>;
|
|
49
49
|
}
|
|
50
|
+
export type BusyMessageResult = {
|
|
51
|
+
kind: "handled";
|
|
52
|
+
} | {
|
|
53
|
+
kind: "requeue";
|
|
54
|
+
text: string;
|
|
55
|
+
};
|
|
50
56
|
export interface DingTalkHandler {
|
|
51
57
|
isRunning(channelId: string): boolean;
|
|
52
58
|
handleEvent(event: DingTalkEvent, bot: DingTalkBot, isEvent?: boolean): Promise<void>;
|
|
53
59
|
handleStop(channelId: string, bot: DingTalkBot): Promise<void>;
|
|
54
|
-
handleBusyMessage(event: DingTalkEvent, bot: DingTalkBot, mode: BusyMessageMode, queueText: string): Promise<
|
|
60
|
+
handleBusyMessage(event: DingTalkEvent, bot: DingTalkBot, mode: BusyMessageMode, queueText: string): Promise<BusyMessageResult>;
|
|
55
61
|
}
|
|
56
62
|
export declare class DingTalkBot {
|
|
57
63
|
private handler;
|
package/dist/runtime/dingtalk.js
CHANGED
|
@@ -481,11 +481,12 @@ export class DingTalkBot {
|
|
|
481
481
|
});
|
|
482
482
|
return true;
|
|
483
483
|
}
|
|
484
|
-
enqueueStreamMessage(event) {
|
|
484
|
+
enqueueStreamMessage(event, textOverride) {
|
|
485
|
+
const queuedEvent = textOverride === undefined ? event : { ...event, text: textOverride };
|
|
485
486
|
this.getQueue(event.channelId).enqueue(async () => {
|
|
486
487
|
this.activeMessageProcessing = true;
|
|
487
488
|
try {
|
|
488
|
-
await this.handler.handleEvent(
|
|
489
|
+
await this.handler.handleEvent(queuedEvent, this);
|
|
489
490
|
}
|
|
490
491
|
finally {
|
|
491
492
|
this.activeMessageProcessing = false;
|
|
@@ -870,16 +871,16 @@ export class DingTalkBot {
|
|
|
870
871
|
return;
|
|
871
872
|
}
|
|
872
873
|
if (builtInCommand?.name === "steer") {
|
|
873
|
-
const
|
|
874
|
-
if (
|
|
875
|
-
this.enqueueStreamMessage(event);
|
|
874
|
+
const result = await this.handler.handleBusyMessage(event, this, "steer", builtInCommand.args);
|
|
875
|
+
if (result.kind === "requeue") {
|
|
876
|
+
this.enqueueStreamMessage(event, result.text);
|
|
876
877
|
}
|
|
877
878
|
return;
|
|
878
879
|
}
|
|
879
880
|
if (builtInCommand?.name === "followup") {
|
|
880
|
-
const
|
|
881
|
-
if (
|
|
882
|
-
this.enqueueStreamMessage(event);
|
|
881
|
+
const result = await this.handler.handleBusyMessage(event, this, "followUp", builtInCommand.args);
|
|
882
|
+
if (result.kind === "requeue") {
|
|
883
|
+
this.enqueueStreamMessage(event, result.text);
|
|
883
884
|
}
|
|
884
885
|
return;
|
|
885
886
|
}
|
|
@@ -891,9 +892,9 @@ export class DingTalkBot {
|
|
|
891
892
|
await this.sendPlain(channelId, "A task is already running. Only `/stop`, `/steer <message>`, and `/followup <message>` are available while streaming.");
|
|
892
893
|
return;
|
|
893
894
|
}
|
|
894
|
-
const
|
|
895
|
-
if (
|
|
896
|
-
this.enqueueStreamMessage(event);
|
|
895
|
+
const result = await this.handler.handleBusyMessage(event, this, this.busyMessageDefault, content);
|
|
896
|
+
if (result.kind === "requeue") {
|
|
897
|
+
this.enqueueStreamMessage(event, result.text);
|
|
897
898
|
}
|
|
898
899
|
return;
|
|
899
900
|
}
|
package/package.json
CHANGED