@zhin.js/service-activity-feedback 3.0.15 → 3.0.17
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/README.md +45 -13
- package/lib/ai-event-binder.d.ts +2 -1
- package/lib/ai-event-binder.d.ts.map +1 -1
- package/lib/ai-event-binder.js +131 -11
- package/lib/ai-event-binder.js.map +1 -1
- package/lib/config.d.ts +1 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/executor.d.ts +4 -4
- package/lib/executor.d.ts.map +1 -1
- package/lib/executor.js +65 -31
- package/lib/executor.js.map +1 -1
- package/lib/orchestrator.d.ts +8 -0
- package/lib/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator.js +60 -2
- package/lib/orchestrator.js.map +1 -1
- package/lib/policy.d.ts.map +1 -1
- package/lib/policy.js +12 -1
- package/lib/policy.js.map +1 -1
- package/package.json +6 -6
- package/plugin.js +16 -8
- package/schema.json +121 -5
- package/src/ai-event-binder.ts +143 -10
- package/src/config.ts +1 -1
- package/src/executor.ts +82 -39
- package/src/orchestrator.ts +80 -2
- package/src/policy.ts +16 -0
package/lib/orchestrator.d.ts
CHANGED
|
@@ -9,10 +9,18 @@ export declare class ActivityFeedbackOrchestrator {
|
|
|
9
9
|
private readonly policy;
|
|
10
10
|
private readonly executor;
|
|
11
11
|
private readonly log;
|
|
12
|
+
private readonly active;
|
|
13
|
+
private readonly transient;
|
|
14
|
+
private readonly pendingCleanup;
|
|
12
15
|
constructor(policy: ActivityFeedbackPolicy, executor: ActivityFeedbackExecutor, log: Logger);
|
|
13
16
|
startPhase(payload: AIEventPayload, phase: ActivityFeedbackPhase, reason: string): Promise<void>;
|
|
14
17
|
stopPhase(payload: AIEventPayload, phase: ActivityFeedbackPhase, reason: string): Promise<void>;
|
|
15
18
|
updateThinkingText(payload: AIEventPayload, text: string): Promise<void>;
|
|
19
|
+
updatePhaseText(payload: AIEventPayload, phase: ActivityFeedbackPhase, text: string): Promise<void>;
|
|
20
|
+
showTransientPhase(payload: AIEventPayload, phase: ActivityFeedbackPhase, reason: string): Promise<void>;
|
|
21
|
+
dispose(): Promise<void>;
|
|
22
|
+
private phaseKey;
|
|
23
|
+
private clearTransient;
|
|
16
24
|
}
|
|
17
25
|
export {};
|
|
18
26
|
//# sourceMappingURL=orchestrator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,KAAK,MAAM,GAAG;IAAE,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CAAE,CAAC;AAErH,qBAAa,4BAA4B;
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,KAAK,MAAM,GAAG;IAAE,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CAAE,CAAC;AAErH,qBAAa,4BAA4B;IAarC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAdtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGlB;IACL,OAAO,CAAC,QAAQ,CAAC,SAAS,CAIrB;IACL,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;gBAGxC,MAAM,EAAE,sBAAsB,EAC9B,QAAQ,EAAE,wBAAwB,EAClC,GAAG,EAAE,MAAM;IAGxB,UAAU,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqChG,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe/F,kBAAkB,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxE,eAAe,CACnB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,qBAAqB,EAC5B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC;IAWV,kBAAkB,CACtB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IAyBV,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAY9B,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,cAAc;CAMvB"}
|
package/lib/orchestrator.js
CHANGED
|
@@ -3,6 +3,9 @@ export class ActivityFeedbackOrchestrator {
|
|
|
3
3
|
policy;
|
|
4
4
|
executor;
|
|
5
5
|
log;
|
|
6
|
+
active = new Map();
|
|
7
|
+
transient = new Map();
|
|
8
|
+
pendingCleanup = new Set();
|
|
6
9
|
constructor(policy, executor, log) {
|
|
7
10
|
this.policy = policy;
|
|
8
11
|
this.executor = executor;
|
|
@@ -31,6 +34,7 @@ export class ActivityFeedbackOrchestrator {
|
|
|
31
34
|
const config = applySubagentActivityPrefixToConfig(resolution.config, payload);
|
|
32
35
|
this.log.debug(`[ActivityFeedback] start ${phase} (${reason}) session=${ctx.sessionId}`);
|
|
33
36
|
await this.executor.start(ctx, phase, config);
|
|
37
|
+
this.active.set(this.phaseKey(ctx, phase), { ctx, phase });
|
|
34
38
|
}
|
|
35
39
|
catch (error) {
|
|
36
40
|
this.log.error(`[ActivityFeedback] start ${phase} failed (${reason}):`, error);
|
|
@@ -40,6 +44,9 @@ export class ActivityFeedbackOrchestrator {
|
|
|
40
44
|
const ctx = toActivityFeedbackEventContext(payload);
|
|
41
45
|
if (!ctx)
|
|
42
46
|
return;
|
|
47
|
+
const key = this.phaseKey(ctx, phase);
|
|
48
|
+
this.clearTransient(key);
|
|
49
|
+
this.active.delete(key);
|
|
43
50
|
try {
|
|
44
51
|
this.log.debug(`[ActivityFeedback] stop ${phase} (${reason}) session=${ctx.sessionId}`);
|
|
45
52
|
await this.executor.stop(ctx, phase);
|
|
@@ -49,15 +56,66 @@ export class ActivityFeedbackOrchestrator {
|
|
|
49
56
|
}
|
|
50
57
|
}
|
|
51
58
|
async updateThinkingText(payload, text) {
|
|
59
|
+
await this.updatePhaseText(payload, 'thinking', text);
|
|
60
|
+
}
|
|
61
|
+
async updatePhaseText(payload, phase, text) {
|
|
52
62
|
const ctx = toActivityFeedbackEventContext(payload);
|
|
53
63
|
if (!ctx || !text)
|
|
54
64
|
return;
|
|
55
65
|
try {
|
|
56
|
-
await this.executor.
|
|
66
|
+
await this.executor.updateText(ctx, phase, withSubagentActivityPrefix(text, payload));
|
|
57
67
|
}
|
|
58
68
|
catch (error) {
|
|
59
|
-
this.log.error(
|
|
69
|
+
this.log.error(`[ActivityFeedback] ${phase} update failed:`, error);
|
|
60
70
|
}
|
|
61
71
|
}
|
|
72
|
+
async showTransientPhase(payload, phase, reason) {
|
|
73
|
+
await this.startPhase(payload, phase, reason);
|
|
74
|
+
const ctx = toActivityFeedbackEventContext(payload);
|
|
75
|
+
if (!ctx)
|
|
76
|
+
return;
|
|
77
|
+
const resolution = this.policy.resolvePhase(ctx.platform, ctx.endpointKey, phase, ctx.sceneType);
|
|
78
|
+
if (resolution.kind !== 'active')
|
|
79
|
+
return;
|
|
80
|
+
const delay = resolution.config.removeDelay ?? 3_000;
|
|
81
|
+
const key = this.phaseKey(ctx, phase);
|
|
82
|
+
this.clearTransient(key);
|
|
83
|
+
const timer = setTimeout(() => {
|
|
84
|
+
this.transient.delete(key);
|
|
85
|
+
this.active.delete(key);
|
|
86
|
+
const cleanup = this.executor.stop(ctx, phase)
|
|
87
|
+
.catch((error) => {
|
|
88
|
+
this.log.error(`[ActivityFeedback] transient ${phase} cleanup failed:`, error);
|
|
89
|
+
})
|
|
90
|
+
.finally(() => {
|
|
91
|
+
this.pendingCleanup.delete(cleanup);
|
|
92
|
+
});
|
|
93
|
+
this.pendingCleanup.add(cleanup);
|
|
94
|
+
}, Math.max(0, delay));
|
|
95
|
+
timer.unref?.();
|
|
96
|
+
this.transient.set(key, { timer, ctx, phase });
|
|
97
|
+
}
|
|
98
|
+
async dispose() {
|
|
99
|
+
const pending = [...this.active.values()];
|
|
100
|
+
const timers = [...this.transient.values()];
|
|
101
|
+
this.active.clear();
|
|
102
|
+
this.transient.clear();
|
|
103
|
+
for (const item of timers)
|
|
104
|
+
clearTimeout(item.timer);
|
|
105
|
+
await Promise.allSettled([
|
|
106
|
+
...pending.map((item) => this.executor.stop(item.ctx, item.phase)),
|
|
107
|
+
...this.pendingCleanup,
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
phaseKey(ctx, phase) {
|
|
111
|
+
return JSON.stringify([ctx.platform, ctx.endpointKey, ctx.sessionId, phase, ctx.messageId]);
|
|
112
|
+
}
|
|
113
|
+
clearTransient(key) {
|
|
114
|
+
const current = this.transient.get(key);
|
|
115
|
+
if (!current)
|
|
116
|
+
return;
|
|
117
|
+
clearTimeout(current.timer);
|
|
118
|
+
this.transient.delete(key);
|
|
119
|
+
}
|
|
62
120
|
}
|
|
63
121
|
//# sourceMappingURL=orchestrator.js.map
|
package/lib/orchestrator.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,mCAAmC,EACnC,0BAA0B,GAG3B,MAAM,gBAAgB,CAAC;AAMxB,MAAM,OAAO,4BAA4B;
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"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"}
|
package/lib/policy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,mCAAmC,EACzC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,6BAA6B,EAGnC,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,iCAAiC,EAAE,gCAAgC,EAAE,MAAM,aAAa,CAAC;AAElG,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,mCAAmC,CAAA;CAAE,CAAC;AAEpE,qBAAa,sBAAsB;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,6BAA6B;IAEnE,YAAY,CACV,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,qBAAqB,EAC5B,SAAS,EAAE,iBAAiB,GAC3B,eAAe;
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,mCAAmC,EACzC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,6BAA6B,EAGnC,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,iCAAiC,EAAE,gCAAgC,EAAE,MAAM,aAAa,CAAC;AAElG,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,mCAAmC,CAAA;CAAE,CAAC;AAEpE,qBAAa,sBAAsB;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,6BAA6B;IAEnE,YAAY,CACV,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,qBAAqB,EAC5B,SAAS,EAAE,iBAAiB,GAC3B,eAAe;CAuBnB"}
|
package/lib/policy.js
CHANGED
|
@@ -10,7 +10,7 @@ export class ActivityFeedbackPolicy {
|
|
|
10
10
|
if (this.service.enabled === false) {
|
|
11
11
|
return { kind: 'disabled' };
|
|
12
12
|
}
|
|
13
|
-
const policy = mergeActivityFeedbackLayers(this.service.defaults, this.service.platforms?.[platform], this.service.endpoints?.[`${platform}:${endpointKey}`]);
|
|
13
|
+
const policy = mergeActivityFeedbackLayers(this.service.defaults, this.service.platforms?.[platform], this.service.endpoints?.[`${platform}:${endpointKey}`], schedulePhaseLayer(this.service, phase));
|
|
14
14
|
if (policy?.enabled === false) {
|
|
15
15
|
return { kind: 'disabled' };
|
|
16
16
|
}
|
|
@@ -21,4 +21,15 @@ export class ActivityFeedbackPolicy {
|
|
|
21
21
|
return { kind: 'active', config };
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
+
function schedulePhaseLayer(service, phase) {
|
|
25
|
+
const scheduleKey = phase === 'schedule_start'
|
|
26
|
+
? 'start'
|
|
27
|
+
: phase === 'schedule_finish'
|
|
28
|
+
? 'finish'
|
|
29
|
+
: phase === 'schedule_error'
|
|
30
|
+
? 'error'
|
|
31
|
+
: undefined;
|
|
32
|
+
const scenes = scheduleKey ? service.schedule?.phases?.[scheduleKey] : undefined;
|
|
33
|
+
return scenes ? { phases: { [phase]: scenes } } : undefined;
|
|
34
|
+
}
|
|
24
35
|
//# sourceMappingURL=policy.js.map
|
package/lib/policy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kCAAkC,GAInC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,2BAA2B,GAE5B,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,iCAAiC,EAAE,gCAAgC,EAAE,MAAM,aAAa,CAAC;AAOlG,MAAM,OAAO,sBAAsB;IACJ;IAA7B,YAA6B,OAAsC;QAAtC,YAAO,GAAP,OAAO,CAA+B;IAAG,CAAC;IAEvE,YAAY,CACV,QAAgB,EAChB,WAAmB,EACnB,KAA4B,EAC5B,SAA4B;QAE5B,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YACnC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,MAAM,GAAG,2BAA2B,CACxC,IAAI,CAAC,OAAO,CAAC,QAAQ,EACrB,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAC,EAClC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,GAAG,QAAQ,IAAI,WAAW,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kCAAkC,GAInC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,2BAA2B,GAE5B,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,iCAAiC,EAAE,gCAAgC,EAAE,MAAM,aAAa,CAAC;AAOlG,MAAM,OAAO,sBAAsB;IACJ;IAA7B,YAA6B,OAAsC;QAAtC,YAAO,GAAP,OAAO,CAA+B;IAAG,CAAC;IAEvE,YAAY,CACV,QAAgB,EAChB,WAAmB,EACnB,KAA4B,EAC5B,SAA4B;QAE5B,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YACnC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,MAAM,GAAG,2BAA2B,CACxC,IAAI,CAAC,OAAO,CAAC,QAAQ,EACrB,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAC,EAClC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,GAAG,QAAQ,IAAI,WAAW,EAAE,CAAC,EACtD,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CACxC,CAAC;QAEF,IAAI,MAAM,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;YAC9B,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,MAAM,GAAG,kCAAkC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QACtF,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC3B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC1B,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACpC,CAAC;CACF;AAED,SAAS,kBAAkB,CACzB,OAAsC,EACtC,KAA4B;IAE5B,MAAM,WAAW,GAAG,KAAK,KAAK,gBAAgB;QAC5C,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,KAAK,KAAK,iBAAiB;YAC3B,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,KAAK,KAAK,gBAAgB;gBAC1B,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,SAAS,CAAC;IAClB,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/service-activity-feedback",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.17",
|
|
4
4
|
"description": "Activity Feedback 服务插件(Plugin Runtime)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -36,18 +36,18 @@
|
|
|
36
36
|
"directory": "plugins/services/activity-feedback"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@zhin.js/agent": "1.1.
|
|
40
|
-
"@zhin.js/logger": "1.0.
|
|
39
|
+
"@zhin.js/agent": "1.1.16",
|
|
40
|
+
"@zhin.js/logger": "1.0.77"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^26.1.2",
|
|
44
44
|
"typescript": "^6.0.3",
|
|
45
45
|
"vitest": "^4.1.10",
|
|
46
|
-
"zhin.js": "6.0.
|
|
46
|
+
"zhin.js": "6.0.14"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@zhin.js/agent": "1.1.
|
|
50
|
-
"zhin.js": "6.0.
|
|
49
|
+
"@zhin.js/agent": "1.1.16",
|
|
50
|
+
"zhin.js": "6.0.14"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
53
53
|
"zhin.js": {
|
package/plugin.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import { definePlugin, outboundHostToken } from 'zhin.js';
|
|
2
|
+
import { createGenerationAdmissionGate, createToken, definePlugin, outboundHostToken, } from 'zhin.js';
|
|
3
3
|
import { getLogger } from '@zhin.js/logger';
|
|
4
4
|
import { loadActivityFeedbackServiceConfig, } from "./lib/config.js";
|
|
5
5
|
import { bindActivityFeedbackToAIEventBus, createActivityFeedbackOrchestratorForRuntime, } from "./lib/ai-event-binder.js";
|
|
6
|
-
import {
|
|
6
|
+
import { createOutboundEndpointAccess, } from "./lib/executor.js";
|
|
7
7
|
const logger = getLogger('activity-feedback');
|
|
8
|
+
const activityFeedbackAdmissionToken = createToken('zhin.activity-feedback.generation-admission');
|
|
8
9
|
/**
|
|
9
10
|
* Activity Feedback service — Plugin Runtime entry.
|
|
10
11
|
*
|
|
@@ -22,14 +23,21 @@ export default definePlugin({
|
|
|
22
23
|
if (serviceConfig.enabled === false) {
|
|
23
24
|
return;
|
|
24
25
|
}
|
|
25
|
-
const
|
|
26
|
-
?
|
|
27
|
-
:
|
|
26
|
+
const outbound = context.resources.has(outboundHostToken)
|
|
27
|
+
? context.resources.use(outboundHostToken)
|
|
28
|
+
: undefined;
|
|
29
|
+
if (!outbound || typeof outbound.runWithView !== 'function') {
|
|
30
|
+
logger.debug('[ActivityFeedback] disabled: OutboundHost with generation-bound runWithView is required');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const access = createOutboundEndpointAccess(outbound, logger);
|
|
28
34
|
const orchestrator = createActivityFeedbackOrchestratorForRuntime(serviceConfig, logger, access);
|
|
29
|
-
const
|
|
30
|
-
context.
|
|
35
|
+
const admission = createGenerationAdmissionGate();
|
|
36
|
+
context.resources.provide(activityFeedbackAdmissionToken, admission);
|
|
37
|
+
const dispose = bindActivityFeedbackToAIEventBus(orchestrator, admission, outbound.runWithView.bind(outbound));
|
|
38
|
+
context.lifecycle.add(async () => {
|
|
31
39
|
logger.debug('[ActivityFeedback] Disposing Runtime AI event binder');
|
|
32
|
-
dispose();
|
|
40
|
+
await dispose();
|
|
33
41
|
});
|
|
34
42
|
},
|
|
35
43
|
});
|
package/schema.json
CHANGED
|
@@ -3,10 +3,126 @@
|
|
|
3
3
|
"type": "object",
|
|
4
4
|
"additionalProperties": false,
|
|
5
5
|
"properties": {
|
|
6
|
-
"enabled": {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
"enabled": {
|
|
7
|
+
"type": "boolean",
|
|
8
|
+
"default": true,
|
|
9
|
+
"description": "Enable activity feedback for this plugin instance.",
|
|
10
|
+
"x-descriptionZh": "是否启用当前活动反馈插件实例。"
|
|
11
|
+
},
|
|
12
|
+
"defaults": {
|
|
13
|
+
"$ref": "#/$defs/activityFeedback",
|
|
14
|
+
"description": "Default policy for all platforms and Endpoints.",
|
|
15
|
+
"x-descriptionZh": "所有平台与 Endpoint 的默认策略。"
|
|
16
|
+
},
|
|
17
|
+
"platforms": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"x-keyPlaceholder": "<platform>",
|
|
20
|
+
"additionalProperties": { "$ref": "#/$defs/activityFeedback" },
|
|
21
|
+
"description": "Platform overrides keyed by platform name; values use the same shape as defaults.",
|
|
22
|
+
"x-descriptionZh": "按平台名覆盖;值的结构与 defaults 相同。"
|
|
23
|
+
},
|
|
24
|
+
"endpoints": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"x-keyPlaceholder": "<platform:endpointKey>",
|
|
27
|
+
"additionalProperties": { "$ref": "#/$defs/activityFeedback" },
|
|
28
|
+
"description": "Endpoint overrides keyed by platform:endpointKey; values use the same shape as defaults.",
|
|
29
|
+
"x-descriptionZh": "按 platform:endpointKey 覆盖;值的结构与 defaults 相同。"
|
|
30
|
+
},
|
|
31
|
+
"schedule": {
|
|
32
|
+
"$ref": "#/$defs/scheduleFeedback",
|
|
33
|
+
"description": "Schedule-only start, finish, and error feedback.",
|
|
34
|
+
"x-descriptionZh": "仅用于 Schedule 的开始、完成与失败反馈。"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"$defs": {
|
|
38
|
+
"activityFeedback": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"additionalProperties": false,
|
|
41
|
+
"properties": {
|
|
42
|
+
"enabled": {
|
|
43
|
+
"type": "boolean",
|
|
44
|
+
"default": true,
|
|
45
|
+
"description": "Enable this policy layer.",
|
|
46
|
+
"x-descriptionZh": "是否启用当前策略层。"
|
|
47
|
+
},
|
|
48
|
+
"phases": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"additionalProperties": false,
|
|
51
|
+
"description": "Feedback policy keyed by lifecycle phase.",
|
|
52
|
+
"x-descriptionZh": "按生命周期 phase 配置反馈策略。",
|
|
53
|
+
"properties": {
|
|
54
|
+
"queued": { "$ref": "#/$defs/scenePhases" },
|
|
55
|
+
"active": { "$ref": "#/$defs/scenePhases" },
|
|
56
|
+
"thinking": { "$ref": "#/$defs/scenePhases" },
|
|
57
|
+
"schedule_start": { "$ref": "#/$defs/scenePhases" },
|
|
58
|
+
"schedule_finish": { "$ref": "#/$defs/scenePhases" },
|
|
59
|
+
"schedule_error": { "$ref": "#/$defs/scenePhases" }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"scenePhases": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"additionalProperties": false,
|
|
67
|
+
"properties": {
|
|
68
|
+
"private": { "$ref": "#/$defs/phaseConfig" },
|
|
69
|
+
"group": { "$ref": "#/$defs/phaseConfig" },
|
|
70
|
+
"channel": { "$ref": "#/$defs/phaseConfig" }
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"phaseConfig": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"properties": {
|
|
77
|
+
"type": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"enum": ["reaction", "message", "typing", "none"],
|
|
80
|
+
"description": "How the phase is presented.",
|
|
81
|
+
"x-descriptionZh": "该 phase 的呈现方式。"
|
|
82
|
+
},
|
|
83
|
+
"emoji": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"description": "Reaction value for type=reaction.",
|
|
86
|
+
"x-descriptionZh": "type=reaction 时使用的回应值。"
|
|
87
|
+
},
|
|
88
|
+
"message": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"description": "Status text for type=message.",
|
|
91
|
+
"x-descriptionZh": "type=message 时使用的状态文本。"
|
|
92
|
+
},
|
|
93
|
+
"autoRemove": {
|
|
94
|
+
"type": "boolean",
|
|
95
|
+
"default": true,
|
|
96
|
+
"description": "Remove the feedback after the phase stops.",
|
|
97
|
+
"x-descriptionZh": "phase 停止后是否自动移除反馈。"
|
|
98
|
+
},
|
|
99
|
+
"removeDelay": {
|
|
100
|
+
"type": "number",
|
|
101
|
+
"description": "Delay before removal in milliseconds; negative values are normalized to zero at runtime.",
|
|
102
|
+
"x-descriptionZh": "自动移除前等待的毫秒数;负值会在运行时归一化为 0。"
|
|
103
|
+
},
|
|
104
|
+
"platformConfig": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"additionalProperties": true,
|
|
107
|
+
"description": "Platform-specific options.",
|
|
108
|
+
"x-descriptionZh": "平台特定选项。"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"scheduleFeedback": {
|
|
113
|
+
"type": "object",
|
|
114
|
+
"additionalProperties": false,
|
|
115
|
+
"properties": {
|
|
116
|
+
"phases": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"additionalProperties": false,
|
|
119
|
+
"properties": {
|
|
120
|
+
"start": { "$ref": "#/$defs/scenePhases" },
|
|
121
|
+
"finish": { "$ref": "#/$defs/scenePhases" },
|
|
122
|
+
"error": { "$ref": "#/$defs/scenePhases" }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
11
127
|
}
|
|
12
128
|
}
|
package/src/ai-event-binder.ts
CHANGED
|
@@ -3,8 +3,11 @@ import {
|
|
|
3
3
|
activityFeedbackAiBus,
|
|
4
4
|
isActivityFeedbackEnabled,
|
|
5
5
|
type AIEventHandlers,
|
|
6
|
+
type AIEventPayload,
|
|
7
|
+
type AIEventTarget,
|
|
6
8
|
} from '@zhin.js/agent';
|
|
7
9
|
import { loadActivityFeedbackServiceConfig, type ActivityFeedbackServiceConfig } from './config.js';
|
|
10
|
+
import type { GenerationAdmissionGate } from 'zhin.js';
|
|
8
11
|
import {
|
|
9
12
|
ActivityFeedbackExecutor,
|
|
10
13
|
createNoopEndpointAccess,
|
|
@@ -29,7 +32,13 @@ export function createActivityFeedbackAIEventHandlers(
|
|
|
29
32
|
onProcessingStart: async (payload) => {
|
|
30
33
|
if (!isActivityFeedbackEnabled(payload, 'active')) return;
|
|
31
34
|
await orchestrator.stopPhase(payload, 'queued', 'processing.start');
|
|
35
|
+
if (typeof payload.iterations === 'number' && payload.iterations > 1) {
|
|
36
|
+
await orchestrator.stopPhase(payload, 'thinking', 'processing.iteration');
|
|
37
|
+
}
|
|
32
38
|
await orchestrator.startPhase(payload, 'active', 'processing.start');
|
|
39
|
+
if (typeof payload.iterations === 'number' && payload.iterations > 1 && payload.content?.trim()) {
|
|
40
|
+
await orchestrator.updatePhaseText(payload, 'active', payload.content.trim());
|
|
41
|
+
}
|
|
33
42
|
},
|
|
34
43
|
|
|
35
44
|
onTypingStart: async () => {},
|
|
@@ -47,9 +56,10 @@ export function createActivityFeedbackAIEventHandlers(
|
|
|
47
56
|
await orchestrator.stopPhase(payload, 'active', 'processing.error');
|
|
48
57
|
},
|
|
49
58
|
|
|
50
|
-
onTypingStop: (payload) => {
|
|
59
|
+
onTypingStop: async (payload) => {
|
|
51
60
|
if (!isActivityFeedbackEnabled(payload, 'active')) return;
|
|
52
|
-
|
|
61
|
+
await orchestrator.stopPhase(payload, 'thinking', 'typing.stop');
|
|
62
|
+
await orchestrator.stopPhase(payload, 'active', 'typing.stop');
|
|
53
63
|
},
|
|
54
64
|
|
|
55
65
|
onThinking: async (payload) => {
|
|
@@ -60,9 +70,26 @@ export function createActivityFeedbackAIEventHandlers(
|
|
|
60
70
|
await orchestrator.updateThinkingText(payload, payload.thinking);
|
|
61
71
|
},
|
|
62
72
|
|
|
73
|
+
onToolCall: async (payload) => {
|
|
74
|
+
if (!isActivityFeedbackEnabled(payload, 'active') || !payload.toolName?.trim()) return;
|
|
75
|
+
const text = `调用工具:${payload.toolName.trim()}…`;
|
|
76
|
+
await orchestrator.updatePhaseText(payload, 'thinking', text);
|
|
77
|
+
await orchestrator.updatePhaseText(payload, 'active', text);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
onToolResult: async (payload) => {
|
|
81
|
+
if (!isActivityFeedbackEnabled(payload, 'active') || !payload.toolName?.trim()) return;
|
|
82
|
+
const outcome = payload.status === 'error' || payload.error ? '未完成' : '已完成';
|
|
83
|
+
const text = `工具 ${payload.toolName.trim()} ${outcome},继续处理…`;
|
|
84
|
+
await orchestrator.updatePhaseText(payload, 'thinking', text);
|
|
85
|
+
await orchestrator.updatePhaseText(payload, 'active', text);
|
|
86
|
+
},
|
|
87
|
+
|
|
63
88
|
onSubagentStart: async (payload) => {
|
|
64
89
|
if (!isActivityFeedbackEnabled(payload, 'thinking')) return;
|
|
65
|
-
//
|
|
90
|
+
// The subagent has an isolated session key; remove its own active placeholder
|
|
91
|
+
// before switching to the richer thinking state.
|
|
92
|
+
await orchestrator.stopPhase(payload, 'active', 'subagent.start');
|
|
66
93
|
await orchestrator.startPhase(payload, 'thinking', 'subagent.start');
|
|
67
94
|
const tag = payload.agentId?.trim() || 'subagent';
|
|
68
95
|
const label = payload.label?.trim()
|
|
@@ -72,16 +99,22 @@ export function createActivityFeedbackAIEventHandlers(
|
|
|
72
99
|
},
|
|
73
100
|
|
|
74
101
|
onSubagentFinish: async (payload) => {
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
102
|
+
if (isActivityFeedbackEnabled(payload, 'thinking')) {
|
|
103
|
+
await orchestrator.stopPhase(payload, 'thinking', 'subagent.finish');
|
|
104
|
+
}
|
|
105
|
+
if (isActivityFeedbackEnabled(payload, 'active')) {
|
|
106
|
+
await orchestrator.stopPhase(payload, 'active', 'subagent.finish');
|
|
107
|
+
}
|
|
78
108
|
},
|
|
79
109
|
|
|
80
110
|
onScheduleStart: (payload) => orchestrator.startPhase(payload, 'schedule_start', 'schedule.start'),
|
|
81
|
-
onScheduleFinish: (payload) =>
|
|
111
|
+
onScheduleFinish: async (payload) => {
|
|
112
|
+
await orchestrator.stopPhase(payload, 'schedule_start', 'schedule.finish');
|
|
113
|
+
await orchestrator.showTransientPhase(payload, 'schedule_finish', 'schedule.finish');
|
|
114
|
+
},
|
|
82
115
|
onScheduleError: async (payload) => {
|
|
83
116
|
await orchestrator.stopPhase(payload, 'schedule_start', 'schedule.error');
|
|
84
|
-
await orchestrator.
|
|
117
|
+
await orchestrator.showTransientPhase(payload, 'schedule_error', 'schedule.error');
|
|
85
118
|
},
|
|
86
119
|
};
|
|
87
120
|
}
|
|
@@ -92,11 +125,111 @@ export function createActivityFeedbackAIEventHandlers(
|
|
|
92
125
|
*/
|
|
93
126
|
export function bindActivityFeedbackToAIEventBus(
|
|
94
127
|
orchestrator: ActivityFeedbackOrchestrator,
|
|
95
|
-
|
|
96
|
-
|
|
128
|
+
admission: GenerationAdmissionGate,
|
|
129
|
+
runWithView: <T>(operation: () => Promise<T>) => Promise<T>,
|
|
130
|
+
): () => Promise<void> {
|
|
131
|
+
let stopWatchingRetirement: (() => void) | undefined;
|
|
132
|
+
let cleanupNeedsGenerationView = false;
|
|
133
|
+
const watchRetirement = () => {
|
|
134
|
+
if (stopWatchingRetirement) return;
|
|
135
|
+
cleanupNeedsGenerationView = true;
|
|
136
|
+
stopWatchingRetirement = admission.onDeactivate(() => {
|
|
137
|
+
void close();
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
const serialized = createGenerationSerializedTarget(
|
|
97
141
|
activityFeedbackAiBus,
|
|
142
|
+
admission,
|
|
143
|
+
runWithView,
|
|
144
|
+
watchRetirement,
|
|
145
|
+
);
|
|
146
|
+
const unsubscribe = subscribeAIEventsOnTarget(
|
|
147
|
+
serialized.target,
|
|
98
148
|
createActivityFeedbackAIEventHandlers(orchestrator),
|
|
99
149
|
);
|
|
150
|
+
let shutdown: Promise<void> | undefined;
|
|
151
|
+
function close(): Promise<void> {
|
|
152
|
+
if (shutdown) return shutdown;
|
|
153
|
+
const cleanup = async () => {
|
|
154
|
+
unsubscribe();
|
|
155
|
+
await serialized.close();
|
|
156
|
+
await orchestrator.dispose();
|
|
157
|
+
};
|
|
158
|
+
// Retirement is published before SnapshotStore changes its current pointer.
|
|
159
|
+
// Enter the IM view synchronously here so timers, native-typing keepalives,
|
|
160
|
+
// and final reaction/message cleanup all remain on this generation's Endpoint.
|
|
161
|
+
// A candidate rolled back before admitting any event has no generation-local
|
|
162
|
+
// feedback state, and its view is unavailable while Root is still idle.
|
|
163
|
+
shutdown = cleanupNeedsGenerationView ? runWithView(cleanup) : cleanup();
|
|
164
|
+
return shutdown;
|
|
165
|
+
}
|
|
166
|
+
return async () => {
|
|
167
|
+
stopWatchingRetirement?.();
|
|
168
|
+
await close();
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Generation-local ordering: one queue per IM session, no module-level runtime state. */
|
|
173
|
+
function createGenerationSerializedTarget(
|
|
174
|
+
source: AIEventTarget,
|
|
175
|
+
admission: GenerationAdmissionGate,
|
|
176
|
+
runWithView: <T>(operation: () => Promise<T>) => Promise<T>,
|
|
177
|
+
onAdmitted: () => void,
|
|
178
|
+
): {
|
|
179
|
+
target: AIEventTarget;
|
|
180
|
+
close(): Promise<void>;
|
|
181
|
+
} {
|
|
182
|
+
const tails = new Map<string, Promise<void>>();
|
|
183
|
+
const wrappers = new Map<(payload: AIEventPayload) => void | Promise<void>,
|
|
184
|
+
(payload: AIEventPayload) => Promise<void>>();
|
|
185
|
+
let closed = false;
|
|
186
|
+
const target: AIEventTarget = {
|
|
187
|
+
on(event, listener) {
|
|
188
|
+
const wrapped = async (payload: AIEventPayload) => {
|
|
189
|
+
const release = admission.acquire();
|
|
190
|
+
if (!release) return;
|
|
191
|
+
onAdmitted();
|
|
192
|
+
try {
|
|
193
|
+
await runWithView(async () => {
|
|
194
|
+
const key = payload.sessionId || '__global__';
|
|
195
|
+
const previous = tails.get(key);
|
|
196
|
+
const run = async () => {
|
|
197
|
+
if (!closed) await listener(payload);
|
|
198
|
+
};
|
|
199
|
+
const current = previous
|
|
200
|
+
? previous.catch(() => undefined).then(run)
|
|
201
|
+
: run();
|
|
202
|
+
tails.set(key, current);
|
|
203
|
+
try {
|
|
204
|
+
await current;
|
|
205
|
+
} finally {
|
|
206
|
+
if (tails.get(key) === current) tails.delete(key);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
} finally {
|
|
210
|
+
release();
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
wrappers.set(listener, wrapped);
|
|
214
|
+
return source.on(event, wrapped);
|
|
215
|
+
},
|
|
216
|
+
off(event, listener) {
|
|
217
|
+
const wrapped = wrappers.get(listener);
|
|
218
|
+
if (!wrapped) return source.off(event, listener);
|
|
219
|
+
wrappers.delete(listener);
|
|
220
|
+
return source.off(event, wrapped);
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
return {
|
|
224
|
+
target,
|
|
225
|
+
async close() {
|
|
226
|
+
closed = true;
|
|
227
|
+
const pending = [...tails.values()];
|
|
228
|
+
await Promise.allSettled(pending);
|
|
229
|
+
tails.clear();
|
|
230
|
+
wrappers.clear();
|
|
231
|
+
},
|
|
232
|
+
};
|
|
100
233
|
}
|
|
101
234
|
|
|
102
235
|
export type ActivityFeedbackLogger = {
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ActivityFeedbackConfig } from '@zhin.js/agent';
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/** Plugin Runtime 实例配置(`plugins.<instanceKey>`,与 Adapter endpoint 配置解耦)。 */
|
|
4
4
|
export interface ActivityFeedbackServiceConfig {
|
|
5
5
|
enabled?: boolean;
|
|
6
6
|
defaults?: ActivityFeedbackConfig;
|