@lemoncat7/dsh-partner 2.11.6 → 2.21.5
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 +3 -0
- package/docs/character-cards-and-renewal.md +1 -1
- package/docs/qq-attention.md +51 -0
- package/docs/qq-channel.md +80 -0
- package/docs/releases/2.11.7.md +9 -0
- package/docs/releases/2.12.0.md +14 -0
- package/docs/releases/2.13.0.md +9 -0
- package/docs/releases/2.14.0.md +9 -0
- package/docs/releases/2.15.0.md +9 -0
- package/docs/releases/2.15.1.md +9 -0
- package/docs/releases/2.16.0.md +18 -0
- package/docs/releases/2.17.0.md +23 -0
- package/docs/releases/2.17.1.md +9 -0
- package/docs/releases/2.18.0.md +9 -0
- package/docs/releases/2.18.1.md +5 -0
- package/docs/releases/2.19.0.md +14 -0
- package/docs/releases/2.19.1.md +7 -0
- package/docs/releases/2.19.2.md +9 -0
- package/docs/releases/2.19.3.md +5 -0
- package/docs/releases/2.20.0.md +10 -0
- package/docs/releases/2.20.1.md +8 -0
- package/docs/releases/2.21.0.md +7 -0
- package/docs/releases/2.21.1.md +8 -0
- package/docs/releases/2.21.2.md +10 -0
- package/docs/releases/2.21.3.md +7 -0
- package/docs/releases/2.21.4.md +8 -0
- package/docs/releases/2.21.5.md +15 -0
- package/docs/tool-entry-compatibility.md +11 -0
- package/lib/agent-runtime.d.ts +17 -0
- package/lib/agent-runtime.d.ts.map +1 -1
- package/lib/agent-runtime.js +313 -52
- package/lib/agent-runtime.js.map +1 -1
- package/lib/api.d.ts +1 -0
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js +93 -27
- package/lib/api.js.map +1 -1
- package/lib/channel-message.d.ts +14 -0
- package/lib/channel-message.d.ts.map +1 -1
- package/lib/channel-message.js.map +1 -1
- package/lib/channels/delivery-policy.js +1 -1
- package/lib/channels/delivery-policy.js.map +1 -1
- package/lib/channels/direct/dispatcher.d.ts +2 -2
- package/lib/channels/direct/dispatcher.d.ts.map +1 -1
- package/lib/channels/direct/dispatcher.js +13 -4
- package/lib/channels/direct/dispatcher.js.map +1 -1
- package/lib/channels/direct/login.d.ts.map +1 -1
- package/lib/channels/direct/login.js +4 -0
- package/lib/channels/direct/login.js.map +1 -1
- package/lib/channels/direct/media.d.ts +2 -0
- package/lib/channels/direct/media.d.ts.map +1 -1
- package/lib/channels/direct/media.js +27 -23
- package/lib/channels/direct/media.js.map +1 -1
- package/lib/channels/direct/message-format.js +127 -127
- package/lib/channels/direct/message-format.js.map +1 -1
- package/lib/channels/direct/transport.d.ts +26 -3
- package/lib/channels/direct/transport.d.ts.map +1 -1
- package/lib/channels/direct/transport.js +31 -5
- package/lib/channels/direct/transport.js.map +1 -1
- package/lib/channels/manager.d.ts +26 -0
- package/lib/channels/manager.d.ts.map +1 -1
- package/lib/channels/manager.js +538 -148
- package/lib/channels/manager.js.map +1 -1
- package/lib/channels/notification-delivery.d.ts +4 -0
- package/lib/channels/notification-delivery.d.ts.map +1 -1
- package/lib/channels/notification-delivery.js +15 -5
- package/lib/channels/notification-delivery.js.map +1 -1
- package/lib/channels/notification-route.d.ts.map +1 -1
- package/lib/channels/notification-route.js +10 -4
- package/lib/channels/notification-route.js.map +1 -1
- package/lib/channels/notification-timeout.d.ts.map +1 -1
- package/lib/channels/notification-timeout.js +2 -0
- package/lib/channels/notification-timeout.js.map +1 -1
- package/lib/channels/outbound-media.d.ts.map +1 -1
- package/lib/channels/outbound-media.js +2 -0
- package/lib/channels/outbound-media.js.map +1 -1
- package/lib/channels/qq/action-catalog.d.ts +38 -0
- package/lib/channels/qq/action-catalog.d.ts.map +1 -0
- package/lib/channels/qq/action-catalog.js +2731 -0
- package/lib/channels/qq/action-catalog.js.map +1 -0
- package/lib/channels/qq/actions.d.ts +21 -0
- package/lib/channels/qq/actions.d.ts.map +1 -0
- package/lib/channels/qq/actions.js +226 -0
- package/lib/channels/qq/actions.js.map +1 -0
- package/lib/channels/qq/affection-adapter.d.ts +3 -0
- package/lib/channels/qq/affection-adapter.d.ts.map +1 -0
- package/lib/channels/qq/affection-adapter.js +16 -0
- package/lib/channels/qq/affection-adapter.js.map +1 -0
- package/lib/channels/qq/attention-classifier.d.ts +10 -0
- package/lib/channels/qq/attention-classifier.d.ts.map +1 -0
- package/lib/channels/qq/attention-classifier.js +62 -0
- package/lib/channels/qq/attention-classifier.js.map +1 -0
- package/lib/channels/qq/authority.d.ts +10 -0
- package/lib/channels/qq/authority.d.ts.map +1 -0
- package/lib/channels/qq/authority.js +50 -0
- package/lib/channels/qq/authority.js.map +1 -0
- package/lib/channels/qq/autonomy.d.ts +30 -0
- package/lib/channels/qq/autonomy.d.ts.map +1 -0
- package/lib/channels/qq/autonomy.js +88 -0
- package/lib/channels/qq/autonomy.js.map +1 -0
- package/lib/channels/qq/catalog.d.ts +14 -0
- package/lib/channels/qq/catalog.d.ts.map +1 -0
- package/lib/channels/qq/catalog.js +34 -0
- package/lib/channels/qq/catalog.js.map +1 -0
- package/lib/channels/qq/connection.d.ts +28 -0
- package/lib/channels/qq/connection.d.ts.map +1 -0
- package/lib/channels/qq/connection.js +200 -0
- package/lib/channels/qq/connection.js.map +1 -0
- package/lib/channels/qq/identity-binding.d.ts +14 -0
- package/lib/channels/qq/identity-binding.d.ts.map +1 -0
- package/lib/channels/qq/identity-binding.js +58 -0
- package/lib/channels/qq/identity-binding.js.map +1 -0
- package/lib/channels/qq/latency.d.ts +22 -0
- package/lib/channels/qq/latency.d.ts.map +1 -0
- package/lib/channels/qq/latency.js +21 -0
- package/lib/channels/qq/latency.js.map +1 -0
- package/lib/channels/qq/message.d.ts +6 -0
- package/lib/channels/qq/message.d.ts.map +1 -0
- package/lib/channels/qq/message.js +49 -0
- package/lib/channels/qq/message.js.map +1 -0
- package/lib/channels/qq/reply-rules.d.ts +30 -0
- package/lib/channels/qq/reply-rules.d.ts.map +1 -0
- package/lib/channels/qq/reply-rules.js +72 -0
- package/lib/channels/qq/reply-rules.js.map +1 -0
- package/lib/channels/qq/reply-tool.d.ts +6 -0
- package/lib/channels/qq/reply-tool.d.ts.map +1 -0
- package/lib/channels/qq/reply-tool.js +113 -0
- package/lib/channels/qq/reply-tool.js.map +1 -0
- package/lib/channels/qq/settings.d.ts +42 -0
- package/lib/channels/qq/settings.d.ts.map +1 -0
- package/lib/channels/qq/settings.js +68 -0
- package/lib/channels/qq/settings.js.map +1 -0
- package/lib/channels/qq/sticker-adapter.d.ts +5 -0
- package/lib/channels/qq/sticker-adapter.d.ts.map +1 -0
- package/lib/channels/qq/sticker-adapter.js +43 -0
- package/lib/channels/qq/sticker-adapter.js.map +1 -0
- package/lib/channels/qq/transport.d.ts +29 -0
- package/lib/channels/qq/transport.d.ts.map +1 -0
- package/lib/channels/qq/transport.js +187 -0
- package/lib/channels/qq/transport.js.map +1 -0
- package/lib/channels/reply-access.d.ts +25 -0
- package/lib/channels/reply-access.d.ts.map +1 -0
- package/lib/channels/reply-access.js +29 -0
- package/lib/channels/reply-access.js.map +1 -0
- package/lib/channels/weixin/connect.d.ts +3 -1
- package/lib/channels/weixin/connect.d.ts.map +1 -1
- package/lib/character/activity-tool.d.ts +7 -0
- package/lib/character/activity-tool.d.ts.map +1 -0
- package/lib/character/activity-tool.js +38 -0
- package/lib/character/activity-tool.js.map +1 -0
- package/lib/character/affection-adapter.d.ts +13 -0
- package/lib/character/affection-adapter.d.ts.map +1 -0
- package/lib/character/affection-adapter.js +13 -0
- package/lib/character/affection-adapter.js.map +1 -0
- package/lib/character/affection-context.d.ts +7 -0
- package/lib/character/affection-context.d.ts.map +1 -0
- package/lib/character/affection-context.js +33 -0
- package/lib/character/affection-context.js.map +1 -0
- package/lib/character/affection.d.ts +47 -0
- package/lib/character/affection.d.ts.map +1 -0
- package/lib/character/affection.js +41 -0
- package/lib/character/affection.js.map +1 -0
- package/lib/character/conversation-behavior.d.ts +1 -1
- package/lib/character/conversation-behavior.d.ts.map +1 -1
- package/lib/character/conversation-behavior.js +5 -3
- package/lib/character/conversation-behavior.js.map +1 -1
- package/lib/character/domain.d.ts +5 -0
- package/lib/character/domain.d.ts.map +1 -1
- package/lib/character/domain.js +21 -4
- package/lib/character/domain.js.map +1 -1
- package/lib/character/identity-guide.d.ts +4 -0
- package/lib/character/identity-guide.d.ts.map +1 -0
- package/lib/character/identity-guide.js +14 -0
- package/lib/character/identity-guide.js.map +1 -0
- package/lib/character/idle-contact.d.ts +12 -5
- package/lib/character/idle-contact.d.ts.map +1 -1
- package/lib/character/idle-contact.js +50 -21
- package/lib/character/idle-contact.js.map +1 -1
- package/lib/character/post-reply.d.ts +19 -0
- package/lib/character/post-reply.d.ts.map +1 -0
- package/lib/character/post-reply.js +33 -0
- package/lib/character/post-reply.js.map +1 -0
- package/lib/character/preview.d.ts.map +1 -1
- package/lib/character/preview.js +9 -1
- package/lib/character/preview.js.map +1 -1
- package/lib/character/relationship-adapter.d.ts +18 -0
- package/lib/character/relationship-adapter.d.ts.map +1 -0
- package/lib/character/relationship-adapter.js +18 -0
- package/lib/character/relationship-adapter.js.map +1 -0
- package/lib/character/sleep-service.d.ts +35 -0
- package/lib/character/sleep-service.d.ts.map +1 -0
- package/lib/character/sleep-service.js +278 -0
- package/lib/character/sleep-service.js.map +1 -0
- package/lib/character/sleep.d.ts +43 -0
- package/lib/character/sleep.d.ts.map +1 -0
- package/lib/character/sleep.js +36 -0
- package/lib/character/sleep.js.map +1 -0
- package/lib/character/state-policy.d.ts +4 -0
- package/lib/character/state-policy.d.ts.map +1 -0
- package/lib/character/state-policy.js +14 -0
- package/lib/character/state-policy.js.map +1 -0
- package/lib/character/store.d.ts.map +1 -1
- package/lib/character/store.js +3 -3
- package/lib/character/store.js.map +1 -1
- package/lib/chat/conversation.js +1 -1
- package/lib/chat/conversation.js.map +1 -1
- package/lib/chat/protocol.d.ts +8 -0
- package/lib/chat/protocol.d.ts.map +1 -1
- package/lib/chat/protocol.js +24 -5
- package/lib/chat/protocol.js.map +1 -1
- package/lib/chat/sticker-adapter.d.ts +34 -0
- package/lib/chat/sticker-adapter.d.ts.map +1 -0
- package/lib/chat/sticker-adapter.js +30 -0
- package/lib/chat/sticker-adapter.js.map +1 -0
- package/lib/chat/tools.d.ts +2 -1
- package/lib/chat/tools.d.ts.map +1 -1
- package/lib/chat/tools.js +59 -13
- package/lib/chat/tools.js.map +1 -1
- package/lib/client-api.d.ts +21 -1
- package/lib/client-api.d.ts.map +1 -1
- package/lib/client-api.js.map +1 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +6388 -2685
- package/lib/client.js.map +4 -4
- package/lib/collaboration/composition.d.ts +2 -0
- package/lib/collaboration/composition.d.ts.map +1 -1
- package/lib/collaboration/composition.js +32 -10
- package/lib/collaboration/composition.js.map +1 -1
- package/lib/collaboration/tool-discovery.d.ts +12 -0
- package/lib/collaboration/tool-discovery.d.ts.map +1 -0
- package/lib/collaboration/tool-discovery.js +148 -0
- package/lib/collaboration/tool-discovery.js.map +1 -0
- package/lib/companions/identity-tool.d.ts +176 -0
- package/lib/companions/identity-tool.d.ts.map +1 -0
- package/lib/companions/identity-tool.js +67 -0
- package/lib/companions/identity-tool.js.map +1 -0
- package/lib/companions/identity.d.ts +24 -0
- package/lib/companions/identity.d.ts.map +1 -0
- package/lib/companions/identity.js +49 -0
- package/lib/companions/identity.js.map +1 -0
- package/lib/companions/management-tool.d.ts +1 -1
- package/lib/companions/management-tool.d.ts.map +1 -1
- package/lib/companions/management-tool.js +4 -2
- package/lib/companions/management-tool.js.map +1 -1
- package/lib/companions/management.d.ts +21 -9
- package/lib/companions/management.d.ts.map +1 -1
- package/lib/companions/management.js +18 -4
- package/lib/companions/management.js.map +1 -1
- package/lib/companions/service.js +1 -0
- package/lib/companions/service.js.map +1 -1
- package/lib/companions/session-configuration.js +2 -2
- package/lib/companions/session-configuration.js.map +1 -1
- package/lib/domain.d.ts +37 -1
- package/lib/domain.d.ts.map +1 -1
- package/lib/domain.js +6 -0
- package/lib/domain.js.map +1 -1
- package/lib/execution/agent-support.d.ts +1 -1
- package/lib/execution/agent-support.d.ts.map +1 -1
- package/lib/execution/agent-support.js +7 -2
- package/lib/execution/agent-support.js.map +1 -1
- package/lib/execution/operation-tool-access.d.ts +13 -0
- package/lib/execution/operation-tool-access.d.ts.map +1 -0
- package/lib/execution/operation-tool-access.js +49 -0
- package/lib/execution/operation-tool-access.js.map +1 -0
- package/lib/execution/service.d.ts.map +1 -1
- package/lib/execution/service.js +2 -10
- package/lib/execution/service.js.map +1 -1
- package/lib/execution/tool-access-adapter.d.ts +23 -0
- package/lib/execution/tool-access-adapter.d.ts.map +1 -0
- package/lib/execution/tool-access-adapter.js +25 -0
- package/lib/execution/tool-access-adapter.js.map +1 -0
- package/lib/heartbeat.js +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +32 -2
- package/lib/index.js.map +1 -1
- package/lib/memory-domain.d.ts +1 -0
- package/lib/memory-domain.d.ts.map +1 -1
- package/lib/memory-prompt-context.js +1 -0
- package/lib/memory-prompt-context.js.map +1 -1
- package/lib/memory-reflection.d.ts.map +1 -1
- package/lib/memory-reflection.js +16 -5
- package/lib/memory-reflection.js.map +1 -1
- package/lib/memory-scope.d.ts +2 -1
- package/lib/memory-scope.d.ts.map +1 -1
- package/lib/memory-scope.js +3 -1
- package/lib/memory-scope.js.map +1 -1
- package/lib/memory-store.d.ts +1 -1
- package/lib/observation-knowledge-entry.d.ts +2 -0
- package/lib/observation-knowledge-entry.d.ts.map +1 -0
- package/lib/observation-knowledge-entry.js +13 -0
- package/lib/observation-knowledge-entry.js.map +1 -0
- package/lib/observation-loop.d.ts.map +1 -1
- package/lib/observation-loop.js +3 -0
- package/lib/observation-loop.js.map +1 -1
- package/lib/storage/split-state.d.ts +1 -0
- package/lib/storage/split-state.d.ts.map +1 -1
- package/lib/storage/split-state.js +9 -2
- package/lib/storage/split-state.js.map +1 -1
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +7 -0
- package/lib/store.js.map +1 -1
- package/lib/ui/affection-panel.d.ts +10 -0
- package/lib/ui/affection-panel.d.ts.map +1 -0
- package/lib/ui/affection-panel.js +44 -0
- package/lib/ui/affection-panel.js.map +1 -0
- package/lib/ui/capability-editor.d.ts.map +1 -1
- package/lib/ui/capability-editor.js +1 -1
- package/lib/ui/capability-editor.js.map +1 -1
- package/lib/ui/channels-panel.d.ts.map +1 -1
- package/lib/ui/channels-panel.js +13 -9
- package/lib/ui/channels-panel.js.map +1 -1
- package/lib/ui/character-editor.d.ts +3 -1
- package/lib/ui/character-editor.d.ts.map +1 -1
- package/lib/ui/character-editor.js +20 -18
- package/lib/ui/character-editor.js.map +1 -1
- package/lib/ui/character-memory-editor.d.ts +6 -1
- package/lib/ui/character-memory-editor.d.ts.map +1 -1
- package/lib/ui/character-memory-editor.js +17 -11
- package/lib/ui/character-memory-editor.js.map +1 -1
- package/lib/ui/companion-draft.d.ts +3 -0
- package/lib/ui/companion-draft.d.ts.map +1 -1
- package/lib/ui/companion-draft.js +1 -1
- package/lib/ui/companion-draft.js.map +1 -1
- package/lib/ui/identity-editor.d.ts.map +1 -1
- package/lib/ui/identity-editor.js +12 -6
- package/lib/ui/identity-editor.js.map +1 -1
- package/lib/ui/idle-contact-editor.d.ts.map +1 -1
- package/lib/ui/idle-contact-editor.js +3 -2
- package/lib/ui/idle-contact-editor.js.map +1 -1
- package/lib/ui/memory/memory-panel.d.ts.map +1 -1
- package/lib/ui/memory/memory-panel.js +5 -3
- package/lib/ui/memory/memory-panel.js.map +1 -1
- package/lib/ui/push-draft.d.ts +5 -5
- package/lib/ui/qq-capability-settings.d.ts +9 -0
- package/lib/ui/qq-capability-settings.d.ts.map +1 -0
- package/lib/ui/qq-capability-settings.js +24 -0
- package/lib/ui/qq-capability-settings.js.map +1 -0
- package/lib/ui/qq-channel-settings.d.ts +13 -0
- package/lib/ui/qq-channel-settings.d.ts.map +1 -0
- package/lib/ui/qq-channel-settings.js +51 -0
- package/lib/ui/qq-channel-settings.js.map +1 -0
- package/lib/ui/qq-identity-binding.d.ts +9 -0
- package/lib/ui/qq-identity-binding.d.ts.map +1 -0
- package/lib/ui/qq-identity-binding.js +6 -0
- package/lib/ui/qq-identity-binding.js.map +1 -0
- package/lib/ui/qq-voice-reply-settings.d.ts +9 -0
- package/lib/ui/qq-voice-reply-settings.d.ts.map +1 -0
- package/lib/ui/qq-voice-reply-settings.js +23 -0
- package/lib/ui/qq-voice-reply-settings.js.map +1 -0
- package/lib/ui/role-reference.js +1 -1
- package/lib/ui/role-reference.js.map +1 -1
- package/lib/ui/sleep-editor.d.ts +10 -0
- package/lib/ui/sleep-editor.d.ts.map +1 -0
- package/lib/ui/sleep-editor.js +45 -0
- package/lib/ui/sleep-editor.js.map +1 -0
- package/package.json +33 -5
package/lib/agent-runtime.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import { activityPolicyCurrent, postReplyAllowed, postReplyCurrent, postReplyProbability, POST_REPLY_PROMPT } from './character/post-reply.js';
|
|
2
|
+
import { PARTNER_TOOL_ENTRIES } from './collaboration/tool-discovery.js';
|
|
3
|
+
import { setReplyTiming } from './channels/qq/latency.js';
|
|
4
|
+
import { resolveToolAccess, installToolAccessGuard } from './execution/tool-access-adapter.js';
|
|
5
|
+
import { renderIdentityRelationship, relationshipTextForIdentity } from './character/relationship-adapter.js';
|
|
6
|
+
import { affectionEnabled, awardAffection, renderAffection } from './character/affection.js';
|
|
7
|
+
import { affectionTurnEligible, currentAffectionAudience } from './character/affection-context.js';
|
|
8
|
+
import { currentQqIdentityRelation, renderQqIdentityRelation } from './channels/qq/identity-binding.js';
|
|
9
|
+
import { qqSettings, qqSessionTarget } from './channels/qq/settings.js';
|
|
10
|
+
import { renderSleepContext, isSleeping } from './character/sleep.js';
|
|
1
11
|
import { questionOrigin } from './channels/question-origin.js';
|
|
2
12
|
import { renderCharacterContext } from './character/domain.js';
|
|
3
13
|
import { renderConversationTurn } from './character/conversation-behavior.js';
|
|
@@ -12,7 +22,7 @@ import { conversationMemoryScope } from './memory-scope.js';
|
|
|
12
22
|
import { mkdir, realpath, stat, writeFile } from 'node:fs/promises';
|
|
13
23
|
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
14
24
|
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
15
|
-
import { IDLE_CONTACT_NOTICE, idleContactDue, idleContactDay, lastIdleContactUser } from './character/idle-contact.js';
|
|
25
|
+
import { IDLE_CONTACT_NOTICE, idleActivityPrompt, isIdleContactNotice, idleActivityProbability, idleContactDue, idleContactDay, idleContactRouteAllowed, lastIdleContactUser } from './character/idle-contact.js';
|
|
16
26
|
import { scopeOf, scopeParentOf } from '@deepseek-ai/dsh-scope';
|
|
17
27
|
import { SessionConfigurationIndex } from './companions/session-configuration.js';
|
|
18
28
|
import { isMissingSession, SessionRecoveryJobs } from './companions/session-recovery.js';
|
|
@@ -27,6 +37,7 @@ import { concernExecutionNotes } from './concern-execution-notes.js';
|
|
|
27
37
|
import { bindTaskSshAccess } from './collaboration/ssh-task-access.js';
|
|
28
38
|
import { hasConcernReply } from './concern-execution-receipt.js';
|
|
29
39
|
import { executeObservationLoop } from './observation-loop.js';
|
|
40
|
+
import { observationKnowledgeEntryDenial } from './observation-knowledge-entry.js';
|
|
30
41
|
import { waitForBoardTurn } from './tasks/continuation-yield.js';
|
|
31
42
|
import { assistantTextAfter, renderPartnerPersona, renderToolProtocol, resolvePartnerAgentOptions as resolveAgentOptions } from './execution/agent-support.js';
|
|
32
43
|
import { channelReplyPartsAfter, isInternalTaskNotice, isRequirementDeliveryReceipt } from './channels/delivery-policy.js';
|
|
@@ -36,6 +47,7 @@ export { extractOutboundAttachments } from './channels/outbound-media.js';
|
|
|
36
47
|
export { renderToolProtocol, resolvePartnerAgentOptions as resolveAgentOptions } from './execution/agent-support.js';
|
|
37
48
|
const PARTNER_TASK_TIMEOUT_MS = 30 * 60_000;
|
|
38
49
|
const HEARTBEAT_READ_ONLY_TOOLS = new Set([
|
|
50
|
+
'knowledge',
|
|
39
51
|
'knowledge_base_search',
|
|
40
52
|
'knowledge_search',
|
|
41
53
|
'knowledge_read',
|
|
@@ -55,6 +67,7 @@ export class PartnerAgentRuntime {
|
|
|
55
67
|
reflection;
|
|
56
68
|
concerns;
|
|
57
69
|
composer;
|
|
70
|
+
latestPostReplyInput = new Map();
|
|
58
71
|
handles = new Map();
|
|
59
72
|
queues = new Map();
|
|
60
73
|
heartbeatQueues = new Map();
|
|
@@ -65,7 +78,9 @@ export class PartnerAgentRuntime {
|
|
|
65
78
|
restoredCompositionJobs = new Map();
|
|
66
79
|
disposeAgentListener;
|
|
67
80
|
disposeConfigurationListener;
|
|
81
|
+
disposeToolAuthority;
|
|
68
82
|
disposeAssemblyListener;
|
|
83
|
+
autonomousAssemblyContexts = new WeakSet();
|
|
69
84
|
rebuildingAssemblies = new WeakSet();
|
|
70
85
|
configurations;
|
|
71
86
|
characterContexts = new Map();
|
|
@@ -84,20 +99,32 @@ export class PartnerAgentRuntime {
|
|
|
84
99
|
this.concerns = concerns;
|
|
85
100
|
this.composer = composer;
|
|
86
101
|
this.configurations = new SessionConfigurationIndex(store);
|
|
102
|
+
this.disposeToolAuthority = typeof this.ctx.tools?.guard === 'function' ? installToolAccessGuard(this.ctx.tools, () => this.store.snapshot()) : (() => { });
|
|
87
103
|
// Current hosts snapshot providers BEFORE agent/pre-step. Rebuild the first
|
|
88
104
|
// assembly after installing scoped contributions; returning the old snapshot
|
|
89
105
|
// would still omit tools for one request even though registration succeeded.
|
|
90
106
|
this.disposeAssemblyListener = typeof this.ctx.on === 'function'
|
|
91
107
|
? this.ctx.on('system-prompt/assemble', async (_assembly, context, next) => {
|
|
92
|
-
if (!context.agent || !context.signal
|
|
108
|
+
if (!context.agent || !context.signal)
|
|
93
109
|
return next();
|
|
110
|
+
const enforce = (assembly) => {
|
|
111
|
+
if (this.autonomousAssemblyContexts.has(context))
|
|
112
|
+
return assembly;
|
|
113
|
+
const configuration = this.configurations.forSession(context.agent.session.id);
|
|
114
|
+
if (configuration?.companion.toolDiscovery)
|
|
115
|
+
return { ...assembly, tools: assembly.tools.filter(tool => PARTNER_TOOL_ENTRIES.some(name => name === tool.name)) };
|
|
116
|
+
const access = resolveToolAccess({ agent: context.agent, state: this.store.snapshot() });
|
|
117
|
+
return { ...assembly, tools: !access.sendTools ? [] : access.allowedTools ? assembly.tools.filter(tool => access.allowedTools.includes(tool.name)) : assembly.tools };
|
|
118
|
+
};
|
|
119
|
+
if (this.rebuildingAssemblies.has(context))
|
|
120
|
+
return enforce(await next());
|
|
94
121
|
const changed = await this.prepareAgentAssembly(context.agent, context.signal);
|
|
95
122
|
const contextual = await this.prepareConversationContext(context.agent, context.signal);
|
|
96
123
|
if (!changed && !contextual)
|
|
97
|
-
return next();
|
|
124
|
+
return enforce(await next());
|
|
98
125
|
this.rebuildingAssemblies.add(context);
|
|
99
126
|
try {
|
|
100
|
-
return await this.ctx.systemPrompt.assemble(context);
|
|
127
|
+
return enforce(await this.ctx.systemPrompt.assemble(context));
|
|
101
128
|
}
|
|
102
129
|
finally {
|
|
103
130
|
this.rebuildingAssemblies.delete(context);
|
|
@@ -110,6 +137,8 @@ export class PartnerAgentRuntime {
|
|
|
110
137
|
}) : () => { };
|
|
111
138
|
this.disposeAgentListener = typeof this.ctx.on === 'function'
|
|
112
139
|
? this.ctx.on('agent/disposed', ({ agent }) => {
|
|
140
|
+
this.replyPresentations.get(agent)?.();
|
|
141
|
+
this.replyPresentations.delete(agent);
|
|
113
142
|
this.preparedTurns.delete(agent);
|
|
114
143
|
this.characterContexts.get(agent.session.id)?.();
|
|
115
144
|
this.characterContexts.delete(agent.session.id);
|
|
@@ -125,48 +154,138 @@ export class PartnerAgentRuntime {
|
|
|
125
154
|
setQuestionAnswerer(answerer) {
|
|
126
155
|
this.questionAnswerer = answerer;
|
|
127
156
|
}
|
|
157
|
+
/** Cancel pending/claimed continuations as soon as another real input is admitted. */
|
|
158
|
+
async cancelPostReply(companionId, keepUserMessageId) {
|
|
159
|
+
const needsCancel = (r) => r.companionId === companionId && r.postReplyOpportunity
|
|
160
|
+
&& ['pending', 'claimed'].includes(r.postReplyOpportunity.status) && r.postReplyOpportunity.userMessageId !== keepUserMessageId;
|
|
161
|
+
if (!this.store.snapshot().sessions.some(needsCancel))
|
|
162
|
+
return;
|
|
163
|
+
await this.store.update(state => { for (const route of state.sessions)
|
|
164
|
+
if (needsCancel(route))
|
|
165
|
+
route.postReplyOpportunity.status = 'cancelled'; });
|
|
166
|
+
}
|
|
167
|
+
async localReplyDisplayed(source) {
|
|
168
|
+
const state = this.store.snapshot(), route = state.sessions.find(r => r.id === source.routeId && r.sessionId === source.sessionId && r.kind === 'local');
|
|
169
|
+
const companion = state.companions.find(c => c.id === route?.companionId);
|
|
170
|
+
if (!route || !companion || !postReplyAllowed(state, companion, route))
|
|
171
|
+
return;
|
|
172
|
+
const agent = await this.ensureAgent(companion, route);
|
|
173
|
+
if (agent.status !== 'idle')
|
|
174
|
+
await agent.whenIdle();
|
|
175
|
+
await this.replyDelivered(source);
|
|
176
|
+
}
|
|
177
|
+
/** Called only after delivery succeeds; autonomous notices never carry this provenance. */
|
|
178
|
+
async replyDelivered(source) {
|
|
179
|
+
const state = this.store.snapshot(), route = state.sessions.find(r => r.id === source.routeId && r.sessionId === source.sessionId);
|
|
180
|
+
const companion = state.companions.find(c => c.id === route?.companionId);
|
|
181
|
+
if (!route || !companion || !postReplyAllowed(state, companion, route) || this.closed || this.isArchived(route) || this.store.isCompanionRemoving(companion.id))
|
|
182
|
+
return;
|
|
183
|
+
const key = `companion:${companion.id}`;
|
|
184
|
+
if (this.queues.has(key) || this.steeringQueues.has(key))
|
|
185
|
+
return;
|
|
186
|
+
const agent = await this.ensureAgent(companion, route);
|
|
187
|
+
if (agent.status !== 'idle' || (this.latestPostReplyInput.has(companion.id) && this.latestPostReplyInput.get(companion.id) !== source.userMessageId) || lastIdleContactUser(agent.session.snapshotEvents())?.id !== source.userMessageId)
|
|
188
|
+
return;
|
|
189
|
+
await this.store.update(draft => {
|
|
190
|
+
const current = draft.companions.find(c => c.id === companion.id), target = draft.sessions.find(r => r.id === route.id);
|
|
191
|
+
if (!current || !target || target.sessionId !== source.sessionId || !postReplyAllowed(draft, current, target)
|
|
192
|
+
|| target.postReplyOpportunity?.userMessageId === source.userMessageId || agent.status !== 'idle'
|
|
193
|
+
|| this.queues.has(key) || this.steeringQueues.has(key) || (this.latestPostReplyInput.has(companion.id) && this.latestPostReplyInput.get(companion.id) !== source.userMessageId) || lastIdleContactUser(agent.session.snapshotEvents())?.id !== source.userMessageId)
|
|
194
|
+
return;
|
|
195
|
+
// At most one pending continuation per companion; different owner accounts share it.
|
|
196
|
+
for (const other of draft.sessions)
|
|
197
|
+
if (other.companionId === companion.id && other.postReplyOpportunity?.status === 'pending')
|
|
198
|
+
other.postReplyOpportunity.status = 'cancelled';
|
|
199
|
+
const now = Date.now(), probability = postReplyProbability(current);
|
|
200
|
+
target.postReplyOpportunity = { userMessageId: source.userMessageId, sessionId: source.sessionId, policyRevision: current.updatedAt,
|
|
201
|
+
deliveredAt: now, dueAt: now + 20_000 + Math.floor(Math.random() * 70_000), expiresAt: now + 180_000,
|
|
202
|
+
probability, status: Math.random() < probability ? 'pending' : 'missed' };
|
|
203
|
+
});
|
|
204
|
+
}
|
|
128
205
|
/** Use the existing timer and real conversation; never impersonate user input. */
|
|
129
206
|
async idleContact(companionId, timeZone = 'Asia/Shanghai') {
|
|
130
207
|
const key = `companion:${companionId}`;
|
|
131
|
-
if (this.closed || this.
|
|
208
|
+
if (this.closed || this.companionActivity(companionId).some(a => a.kind !== 'memory' && a.kind !== 'observation') || this.queues.has(key) || this.taskQueues.has(key) || this.heartbeatQueues.has(key) || this.steeringQueues.has(key) || this.store.isCompanionRemoving(companionId))
|
|
132
209
|
return;
|
|
133
210
|
const state = this.store.snapshot(), companion = state.companions.find(c => c.id === companionId);
|
|
134
|
-
const
|
|
211
|
+
const pendingRoute = state.sessions.find(r => r.companionId === companionId && r.postReplyOpportunity?.status === 'pending');
|
|
212
|
+
let route = pendingRoute ?? state.sessions.find(r => r.id === companion?.idleContact?.routeId && r.companionId === companionId);
|
|
213
|
+
const continuation = !!pendingRoute;
|
|
214
|
+
if (pendingRoute && companion) {
|
|
215
|
+
const pending = pendingRoute.postReplyOpportunity;
|
|
216
|
+
if (!postReplyCurrent(state, companion, pendingRoute) || Date.now() > pending.expiresAt) {
|
|
217
|
+
await this.cancelPostReply(companionId);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (Date.now() < pending.dueAt)
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
135
223
|
if (!companion?.idleContact?.enabled || !route || this.isArchived(route))
|
|
136
224
|
return;
|
|
225
|
+
const channel = state.channels.find(c => c.id === route.channelId);
|
|
226
|
+
if (channel?.platform === 'qq' && route.qqSessionMode !== (qqSettings(channel.qq).mainSessionPeers.includes(qqSessionTarget(route.userId)) ? 'main' : 'separate'))
|
|
227
|
+
route = await this.ensureSession(companion, route.channelId, route.userId);
|
|
137
228
|
const agent = await this.ensureAgent(companion, route);
|
|
138
|
-
if (agent.status !== 'idle' || this.
|
|
229
|
+
if (agent.status !== 'idle' || this.companionActivity(companionId).some(a => a.kind !== 'memory' && a.kind !== 'observation') || this.queues.has(key) || this.taskQueues.has(key))
|
|
139
230
|
return;
|
|
140
231
|
const user = lastIdleContactUser(agent.session.snapshotEvents());
|
|
141
|
-
|
|
232
|
+
const completed = [...agent.session.snapshotEvents()].reverse().find(e => e.type === 'turn/end');
|
|
233
|
+
if (user && completed)
|
|
234
|
+
user.at = Math.max(user.at, completed.time);
|
|
235
|
+
if (continuation) {
|
|
236
|
+
if (user?.id !== route.postReplyOpportunity?.userMessageId) {
|
|
237
|
+
await this.cancelPostReply(companionId);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else if (!idleContactDue(this.store.snapshot(), companion, user, Date.now(), timeZone))
|
|
142
242
|
return;
|
|
143
243
|
const stableSeq = agent.session.seq;
|
|
144
|
-
const message = createUserMessage({ content: [{ type: 'text', text: '
|
|
244
|
+
const message = createUserMessage({ content: [{ type: 'text', text: idleActivityPrompt(this.store.snapshot(), companion) + (continuation ? '\n' + POST_REPLY_PROMPT : '')
|
|
145
245
|
}], source: { kind: 'plugin:@lemoncat7/dsh-partner', plugin: '@lemoncat7/dsh-partner', form: 'notice', summary: IDLE_CONTACT_NOTICE } });
|
|
146
246
|
let claimed = false;
|
|
147
247
|
await this.store.update(draft => {
|
|
148
248
|
const current = draft.companions.find(c => c.id === companionId), target = draft.sessions.find(r => r.id === route.id);
|
|
149
249
|
const now = Date.now();
|
|
150
|
-
if (!current || current.updatedAt !== companion.updatedAt || !target || target.sessionId !== route.sessionId || !idleContactDue(draft, current, user, now, timeZone))
|
|
250
|
+
if (!current || current.updatedAt !== companion.updatedAt || !target || target.sessionId !== route.sessionId || (continuation ? !postReplyCurrent(draft, current, target, now) || target.postReplyOpportunity?.status !== 'pending' || target.postReplyOpportunity.userMessageId !== user?.id || now > target.postReplyOpportunity.expiresAt : !idleContactDue(draft, current, user, now, timeZone)))
|
|
151
251
|
return;
|
|
252
|
+
const probability = continuation ? target.postReplyOpportunity.probability : idleActivityProbability(draft, current, user, now);
|
|
253
|
+
target.idleActivityCheck = { at: now, probability };
|
|
254
|
+
if (!continuation && Math.random() >= probability)
|
|
255
|
+
return;
|
|
256
|
+
if (continuation)
|
|
257
|
+
target.postReplyOpportunity.status = 'claimed';
|
|
152
258
|
const day = idleContactDay(now, timeZone);
|
|
153
|
-
target.idleContactState = { userMessageId: user.id, messageId: message.id, attemptedAt: now, day, count: (target.idleContactState?.day === day ? target.idleContactState.count : 0) + 1, timeZone, policyRevision: current.updatedAt };
|
|
259
|
+
target.idleContactState = { ...(continuation ? { kind: 'post-reply' } : {}), userMessageId: user.id, messageId: message.id, attemptedAt: now, day, count: (target.idleContactState?.day === day ? target.idleContactState.count : 0) + 1, timeZone, policyRevision: current.updatedAt };
|
|
154
260
|
claimed = true;
|
|
155
261
|
});
|
|
156
262
|
// Claim before generation survives restart. Don't replay uncertain sends or steal active turns.
|
|
157
263
|
if (!claimed || this.closed || this.store.isCompanionRemoving(companionId) || agent.status !== 'idle' || agent.session.seq !== stableSeq || this.queues.has(key) || this.taskQueues.has(key))
|
|
158
264
|
return;
|
|
159
|
-
const
|
|
160
|
-
if (!current
|
|
265
|
+
const currentState = this.store.snapshot(), current = currentState.companions.find(c => c.id === companionId), currentRoute = currentState.sessions.find(r => r.id === route.id);
|
|
266
|
+
if (!current || !currentRoute || !activityPolicyCurrent(currentState, current, currentRoute) || isSleeping(current) || current.updatedAt !== companion.updatedAt || this.isArchived(route))
|
|
161
267
|
return;
|
|
162
268
|
agent.followup(message);
|
|
163
269
|
}
|
|
270
|
+
async idleContactDeliveryCurrent(origin) {
|
|
271
|
+
const state = this.store.snapshot(), route = state.sessions.find(r => r.id === origin.routeId);
|
|
272
|
+
const companion = state.companions.find(c => c.id === route?.companionId);
|
|
273
|
+
if (!route || !companion || route.sessionId !== origin.sessionId || !activityPolicyCurrent(state, companion, route) || companion.updatedAt !== origin.policyRevision || route.idleContactState?.messageId !== origin.messageId || !idleContactRouteAllowed(state, companion, route.id) || isSleeping(companion) || this.isArchived(route) || this.store.isCompanionRemoving(companion.id))
|
|
274
|
+
return false;
|
|
275
|
+
const channel = state.channels.find(c => c.id === route.channelId);
|
|
276
|
+
if (channel?.platform === 'qq' && route.qqSessionMode !== (qqSettings(channel.qq).mainSessionPeers.includes(qqSessionTarget(route.userId)) ? 'main' : 'separate'))
|
|
277
|
+
return false;
|
|
278
|
+
const agent = await this.ensureAgent(companion, route);
|
|
279
|
+
return lastIdleContactUser(agent.session.snapshotEvents())?.id === origin.userMessageId;
|
|
280
|
+
}
|
|
164
281
|
async reply(companion, channelId, userId, message, progress) {
|
|
165
282
|
const key = `companion:${companion.id}`;
|
|
283
|
+
await this.cancelPostReply(companion.id);
|
|
284
|
+
const queuedAt = Date.now();
|
|
166
285
|
const previous = this.queues.get(key) ?? Promise.resolve();
|
|
167
286
|
let output = { text: '', attachments: [] };
|
|
168
287
|
reportProgress(progress, 'queued');
|
|
169
|
-
const current = previous.catch(() => { }).then(async () => { output = await this.drive(companion, channelId, userId, message, progress); });
|
|
288
|
+
const current = previous.catch(() => { }).then(async () => { output = await this.drive(companion, channelId, userId, message, progress, queuedAt); });
|
|
170
289
|
this.queues.set(key, current);
|
|
171
290
|
try {
|
|
172
291
|
await current;
|
|
@@ -178,6 +297,7 @@ export class PartnerAgentRuntime {
|
|
|
178
297
|
}
|
|
179
298
|
}
|
|
180
299
|
async steer(companion, channelId, userId, message) {
|
|
300
|
+
await this.cancelPostReply(companion.id);
|
|
181
301
|
const key = `companion:${companion.id}`;
|
|
182
302
|
const previous = this.steeringQueues.get(key) ?? Promise.resolve();
|
|
183
303
|
let accepted = false;
|
|
@@ -196,6 +316,9 @@ export class PartnerAgentRuntime {
|
|
|
196
316
|
const route = this.store.snapshot().sessions.find(item => item.channelId === channelId && item.userId === userId && item.companionId === companion.id);
|
|
197
317
|
if (route === undefined)
|
|
198
318
|
return false;
|
|
319
|
+
const channel = this.store.snapshot().channels.find(c => c.id === channelId);
|
|
320
|
+
if (channel?.platform === 'qq' && route.qqSessionMode !== (qqSettings(channel.qq).mainSessionPeers.includes(qqSessionTarget(userId)) ? 'main' : 'separate'))
|
|
321
|
+
return false;
|
|
199
322
|
const agent = this.handles.get(route.sessionId)?.agent ?? this.ctx.agents.get(route.sessionId);
|
|
200
323
|
if (agent === undefined || agent.status !== 'running')
|
|
201
324
|
return false;
|
|
@@ -258,7 +381,7 @@ export class PartnerAgentRuntime {
|
|
|
258
381
|
// If there was no local conversation, continue the newest existing channel
|
|
259
382
|
// conversation. Do not concatenate unrelated historical transcripts.
|
|
260
383
|
if (!existing) {
|
|
261
|
-
const previous = state.sessions.filter(item => item.companionId === companion.id && canReuseSession(item, companion.id, this.ctx.workspaceRegistry.archivedSessionIds)).sort((a, b) => b.lastMessageAt - a.lastMessageAt)[0];
|
|
384
|
+
const previous = state.sessions.filter(item => item.companionId === companion.id && !state.channels.some(c => c.id === item.channelId && c.platform === 'qq') && canReuseSession(item, companion.id, this.ctx.workspaceRegistry.archivedSessionIds)).sort((a, b) => b.lastMessageAt - a.lastMessageAt)[0];
|
|
262
385
|
if (previous) {
|
|
263
386
|
route.sessionId = previous.sessionId;
|
|
264
387
|
if (previous.cwd)
|
|
@@ -338,9 +461,16 @@ export class PartnerAgentRuntime {
|
|
|
338
461
|
const agent = await this.ensureAgent(companion, route);
|
|
339
462
|
if (!this.store.snapshot().sessions.some(s => s.id === route.id && s.sessionId === wake.originSessionId) || this.isArchived(route))
|
|
340
463
|
throw new Error('原续接会话已变更');
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
464
|
+
const preflight = assembleContextFor(agent, signal);
|
|
465
|
+
this.autonomousAssemblyContexts.add(preflight);
|
|
466
|
+
try {
|
|
467
|
+
const assembly = await agent.ctx.systemPrompt.assemble(preflight);
|
|
468
|
+
signal.throwIfAborted();
|
|
469
|
+
assertWakeTools(entry, assembly.tools.map(tool => tool.name));
|
|
470
|
+
}
|
|
471
|
+
finally {
|
|
472
|
+
this.autonomousAssemblyContexts.delete(preflight);
|
|
473
|
+
}
|
|
344
474
|
const message = createUserMessage({ content: [{ type: 'text', text: continuationPrompt(entry) }],
|
|
345
475
|
source: { kind: 'plugin:@lemoncat7/dsh-partner', plugin: '@lemoncat7/dsh-partner', form: 'notice', summary: '伙伴长任务续接' } });
|
|
346
476
|
await this.store.update(state => {
|
|
@@ -584,6 +714,14 @@ export class PartnerAgentRuntime {
|
|
|
584
714
|
}
|
|
585
715
|
}
|
|
586
716
|
async observeSessionEvent(session, event) {
|
|
717
|
+
if (event.type === 'user/message' && event.data.source.kind === 'user') {
|
|
718
|
+
const owner = this.store.snapshot().sessions.find(r => r.sessionId === session.id)?.companionId;
|
|
719
|
+
if (owner) {
|
|
720
|
+
this.latestPostReplyInput.set(owner, event.data.id);
|
|
721
|
+
await this.cancelPostReply(owner, event.data.id);
|
|
722
|
+
}
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
587
725
|
if (event.type !== 'turn/end' || event.data.reason.kind !== 'completed')
|
|
588
726
|
return;
|
|
589
727
|
const route = this.store.snapshot().sessions.find(item => item.sessionId === session.id);
|
|
@@ -612,6 +750,7 @@ export class PartnerAgentRuntime {
|
|
|
612
750
|
if (companion === undefined)
|
|
613
751
|
return;
|
|
614
752
|
const events = completedTurnEvents(session.snapshotEvents(), event);
|
|
753
|
+
const identityRelation = currentQqIdentityRelation(session.id, events, this.store.snapshot().sessions, this.store.snapshot().channels ?? []);
|
|
615
754
|
const prompt = events.find(item => item.type === 'user/message' && item.data.source.kind === 'user');
|
|
616
755
|
const replies = events.filter(item => item.type === 'assistant/message' && !item.data.interrupted);
|
|
617
756
|
const userText = events.filter(e => e.type === 'user/message' && e.data.source.kind === 'user').map(e => e.type === 'user/message' ? textContent(e.data.content) : '').filter(Boolean).join('\n\n');
|
|
@@ -620,10 +759,11 @@ export class PartnerAgentRuntime {
|
|
|
620
759
|
if (!userText || !assistantText)
|
|
621
760
|
return;
|
|
622
761
|
const scopeId = memoryScope(route.channelId, route.userId);
|
|
623
|
-
const concernDirective = await this.concerns?.applyUserDirective(companion.id, scopeId, userText, event.time);
|
|
762
|
+
const concernDirective = identityRelation && !identityRelation.matched ? undefined : await this.concerns?.applyUserDirective(companion.id, scopeId, userText, event.time);
|
|
624
763
|
const created = companion.automation.memory.enabled && this.reflection !== undefined ? await this.reflection.reflect(companion, {
|
|
625
764
|
id: `turn-${createHash('sha256').update(JSON.stringify([session.id, event.time, userText])).digest('hex').slice(0, 32)}`, companionId: companion.id, scopeId: conversationMemoryScope(route, this.store.snapshot().sessions),
|
|
626
765
|
characterEnabled: companion.characterCard?.enabled === true,
|
|
766
|
+
...(identityRelation ? { identityRelation } : {}),
|
|
627
767
|
userMessageIds: events.flatMap(e => e.type === 'user/message' && e.data.source.kind === 'user' ? [e.data.id] : []),
|
|
628
768
|
sessionId: session.id, at: event.time, user: userText, assistant: assistantText, concernScopeId: scopeId,
|
|
629
769
|
...(concernDirective === undefined ? {} : { concernDirective }),
|
|
@@ -645,6 +785,9 @@ export class PartnerAgentRuntime {
|
|
|
645
785
|
const target = state.sessions.find(item => item.id === route.id);
|
|
646
786
|
if (target)
|
|
647
787
|
target.lastMessageAt = Date.now();
|
|
788
|
+
const current = state.companions.find(c => c.id === companion.id);
|
|
789
|
+
if (current && affectionEnabled(current) && affectionTurnEligible(state, current.id, session.id, events))
|
|
790
|
+
awardAffection(current, `${session.id}:${event.seq}`, event.time);
|
|
648
791
|
});
|
|
649
792
|
}
|
|
650
793
|
async recordConcernCreatedNotice(companion, scopeId, concerns) {
|
|
@@ -765,6 +908,18 @@ export class PartnerAgentRuntime {
|
|
|
765
908
|
if (this.store.isCompanionRemoving(configuration.companion.id))
|
|
766
909
|
throw new Error('伙伴正在删除,不能启动新轮次');
|
|
767
910
|
signal.throwIfAborted();
|
|
911
|
+
const access = resolveToolAccess({ agent, state: this.store.snapshot() });
|
|
912
|
+
let presentationChanged = false;
|
|
913
|
+
const stableNative = configuration.companion.toolDiscovery || !!access.allowedTools;
|
|
914
|
+
if (stableNative && !this.replyPresentations.has(agent) && typeof agent.ctx.tools?.presentAs === 'function') {
|
|
915
|
+
this.replyPresentations.set(agent, agent.ctx.tools.presentAs('native'));
|
|
916
|
+
presentationChanged = true;
|
|
917
|
+
}
|
|
918
|
+
else if (!stableNative && this.replyPresentations.has(agent)) {
|
|
919
|
+
this.replyPresentations.get(agent)();
|
|
920
|
+
this.replyPresentations.delete(agent);
|
|
921
|
+
presentationChanged = true;
|
|
922
|
+
}
|
|
768
923
|
const installed = this.restoredCompositions.get(agent.session.id);
|
|
769
924
|
if (installed?.agent !== agent || installed.revision !== configuration.revision) {
|
|
770
925
|
const job = (async () => {
|
|
@@ -783,15 +938,20 @@ export class PartnerAgentRuntime {
|
|
|
783
938
|
}
|
|
784
939
|
return true;
|
|
785
940
|
}
|
|
786
|
-
return
|
|
941
|
+
return presentationChanged;
|
|
787
942
|
}
|
|
943
|
+
replyPresentations = new Map();
|
|
788
944
|
contextRevisions = new WeakMap();
|
|
789
945
|
async prepareConversationContext(agent, signal) {
|
|
790
946
|
const config = this.configurations.forSession(agent.session.id);
|
|
791
947
|
if (!config)
|
|
792
948
|
return false;
|
|
793
949
|
const { companion, route } = config;
|
|
794
|
-
|
|
950
|
+
const state = this.store.snapshot();
|
|
951
|
+
const relationEvents = state.channels?.some(c => c.platform === 'qq' && state.sessions.some(r => r.sessionId === agent.session.id && r.channelId === c.id))
|
|
952
|
+
? agent.session.snapshotEvents() : undefined;
|
|
953
|
+
const identityRelation = relationEvents ? currentQqIdentityRelation(agent.session.id, relationEvents, state.sessions, state.channels) : undefined;
|
|
954
|
+
if (!identityRelation && !relationshipTextForIdentity(companion) && (!this.memory || !companion.automation.memory.enabled) && !companion.characterCard?.enabled && !renderSleepContext(companion, route.id)) {
|
|
795
955
|
this.characterContexts.get(agent.session.id)?.();
|
|
796
956
|
this.characterContexts.delete(agent.session.id);
|
|
797
957
|
this.contextRevisions.delete(agent);
|
|
@@ -800,7 +960,11 @@ export class PartnerAgentRuntime {
|
|
|
800
960
|
const messages = agent.session.deriveMessages();
|
|
801
961
|
const last = [...messages].reverse().find(m => m.role === 'user' && m.source.kind === 'user');
|
|
802
962
|
const query = last && last.role === 'user' ? textContent(last.content) : '';
|
|
803
|
-
const
|
|
963
|
+
const events = relationEvents ?? agent.session.snapshotEvents();
|
|
964
|
+
const origin = [...events].reverse().find(e => e.type === 'user/message' && (e.data.source.kind === 'user' || isIdleContactNotice(e) || isInternalTaskNotice(e)));
|
|
965
|
+
const autonomous = Boolean(origin && isIdleContactNotice(origin));
|
|
966
|
+
const affectionAudience = currentAffectionAudience(state, companion.id, agent.session.id, events);
|
|
967
|
+
const revision = `${JSON.stringify(identityRelation)}:${config.revision}:${last?.id ?? ''}:${autonomous && origin?.type === 'user/message' ? origin.data.id : ''}:${companion.sleepState?.dreams?.at(-1)?.id ?? ''}:${affectionAudience}:${companion.affection?.updatedAt ?? 0}`;
|
|
804
968
|
if (this.contextRevisions.get(agent) === revision)
|
|
805
969
|
return false;
|
|
806
970
|
const scope = conversationMemoryScope(route, this.store.snapshot().sessions);
|
|
@@ -814,10 +978,17 @@ export class PartnerAgentRuntime {
|
|
|
814
978
|
consecutiveUserMessages++;
|
|
815
979
|
}
|
|
816
980
|
signal.throwIfAborted();
|
|
817
|
-
const
|
|
981
|
+
const relationshipAllowed = !identityRelation || identityRelation.matched;
|
|
982
|
+
const access = resolveToolAccess({ agent, state });
|
|
983
|
+
const text = [access.allowedTools || !access.sendTools ? access.denialReason + ' 回复能力通过 partner_tools 查询和调用(未开启统一入口时使用原工具);语音能力为 partner_qq_reply;不能代办操作,不承诺使用私有能力。' : '',
|
|
984
|
+
renderIdentityRelationship(companion, identityRelation ? { channel: 'qq', matched: identityRelation.matched } : { channel: 'default' }),
|
|
985
|
+
renderAffection(companion, affectionAudience),
|
|
986
|
+
renderCharacterContext(companion.characterCard, messages.slice(-6).filter(m => m.role === 'user' || m.role === 'assistant').map(m => textContent(m.content)).join('\n').slice(-12000), relationshipAllowed ? story : undefined),
|
|
818
987
|
recalled ? renderMemory(recalled.relevant, recalled.connections, recalled.scenes, recalled.history) : '',
|
|
819
|
-
recalled ? renderProfile(recalled.profile) : '',
|
|
820
|
-
renderConversationTurn(companion.characterCard, consecutiveUserMessages),
|
|
988
|
+
recalled && relationshipAllowed ? renderProfile(recalled.profile) : '',
|
|
989
|
+
renderConversationTurn(companion.characterCard, consecutiveUserMessages, autonomous),
|
|
990
|
+
renderQqIdentityRelation(identityRelation),
|
|
991
|
+
renderSleepContext(companion, this.store.snapshot().sessions.filter(r => r.companionId === companion.id && r.sessionId === route.sessionId).map(r => r.id)),
|
|
821
992
|
].filter(Boolean).join('\n\n');
|
|
822
993
|
this.characterContexts.get(agent.session.id)?.();
|
|
823
994
|
// A changing .section() appends an entire system prompt on in-history routes.
|
|
@@ -907,10 +1078,25 @@ export class PartnerAgentRuntime {
|
|
|
907
1078
|
}
|
|
908
1079
|
return renewed;
|
|
909
1080
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1081
|
+
companionActivity(companionId) {
|
|
1082
|
+
const activity = [];
|
|
1083
|
+
if (this.reflection?.isRunning(companionId))
|
|
1084
|
+
activity.push({ kind: 'memory', label: '后台记忆整理(不阻止群聊判断)' });
|
|
1085
|
+
if (this.observationJobs.has(companionId))
|
|
1086
|
+
activity.push({ kind: 'observation', label: '归档已完成的聊天' });
|
|
1087
|
+
if (this.agentSetupJobs.has(companionId))
|
|
1088
|
+
activity.push({ kind: 'setup', label: '准备伙伴会话' });
|
|
1089
|
+
const seen = new Set();
|
|
1090
|
+
for (const route of this.store.snapshot().sessions.filter(r => r.companionId === companionId)) {
|
|
1091
|
+
if (seen.has(route.sessionId))
|
|
1092
|
+
continue;
|
|
1093
|
+
seen.add(route.sessionId);
|
|
1094
|
+
if ((this.handles.get(route.sessionId)?.agent ?? this.ctx.agents.get(route.sessionId))?.status === 'running')
|
|
1095
|
+
activity.push({ kind: 'conversation', label: route.qqSessionTitle || (route.kind === 'local' ? '主会话正在运行' : '渠道会话正在运行'), sessionId: route.sessionId });
|
|
1096
|
+
}
|
|
1097
|
+
return activity;
|
|
913
1098
|
}
|
|
1099
|
+
isCompanionBusy(companionId) { return this.companionActivity(companionId).length > 0; }
|
|
914
1100
|
async releaseCompanion(companionId) {
|
|
915
1101
|
const sessions = this.store.snapshot().sessions.filter(item => item.companionId === companionId);
|
|
916
1102
|
await Promise.all(sessions.map(async (item) => {
|
|
@@ -936,7 +1122,11 @@ export class PartnerAgentRuntime {
|
|
|
936
1122
|
this.closed = true;
|
|
937
1123
|
this.disposeConfigurationListener();
|
|
938
1124
|
this.disposeAssemblyListener();
|
|
1125
|
+
this.disposeToolAuthority();
|
|
939
1126
|
this.disposeAgentListener();
|
|
1127
|
+
for (const dispose of this.replyPresentations.values())
|
|
1128
|
+
dispose();
|
|
1129
|
+
this.replyPresentations.clear();
|
|
940
1130
|
this.configurations.close();
|
|
941
1131
|
await Promise.allSettled(this.restoredCompositionJobs.values());
|
|
942
1132
|
for (const restored of this.restoredCompositions.values())
|
|
@@ -948,6 +1138,7 @@ export class PartnerAgentRuntime {
|
|
|
948
1138
|
this.restoredCompositionJobs.clear();
|
|
949
1139
|
await Promise.all([...this.handles.values()].map(handle => handle.dispose().catch(() => { })));
|
|
950
1140
|
this.handles.clear();
|
|
1141
|
+
this.latestPostReplyInput.clear();
|
|
951
1142
|
this.heartbeatQueues.clear();
|
|
952
1143
|
this.taskQueues.clear();
|
|
953
1144
|
this.steeringQueues.clear();
|
|
@@ -962,9 +1153,11 @@ export class PartnerAgentRuntime {
|
|
|
962
1153
|
restored?.dispose();
|
|
963
1154
|
this.restoredCompositions.delete(sessionId);
|
|
964
1155
|
}
|
|
965
|
-
async drive(companion, channelId, userId, message, progress) {
|
|
1156
|
+
async drive(companion, channelId, userId, message, progress, queuedAt = Date.now()) {
|
|
966
1157
|
reportProgress(progress, 'working');
|
|
967
|
-
const
|
|
1158
|
+
const enteredAt = Date.now();
|
|
1159
|
+
let submittedAt = enteredAt, modelEndedAt = enteredAt, lastTurnEnd;
|
|
1160
|
+
const session = await this.ensureSession(companion, channelId, userId, message.sessionTitle);
|
|
968
1161
|
const agent = await this.ensureAgent(companion, session);
|
|
969
1162
|
if (agent.status !== 'idle')
|
|
970
1163
|
await agent.whenIdle();
|
|
@@ -976,10 +1169,13 @@ export class PartnerAgentRuntime {
|
|
|
976
1169
|
source: { kind: 'plugin:@lemoncat7/dsh-partner', plugin: '@lemoncat7/dsh-partner', form: 'notice', summary: '伙伴想起了与当前消息相关的挂念' },
|
|
977
1170
|
}));
|
|
978
1171
|
const startSeq = agent.session.seq;
|
|
1172
|
+
let deliveredUserMessageId = '';
|
|
979
1173
|
let directProgress = false;
|
|
980
|
-
const detach =
|
|
1174
|
+
const detach = this.ctx.on('session/event', (current, event) => {
|
|
981
1175
|
if (current.id !== agent.session.id || event.seq < startSeq)
|
|
982
1176
|
return;
|
|
1177
|
+
if (event.type === 'turn/end')
|
|
1178
|
+
lastTurnEnd = event.time;
|
|
983
1179
|
if (event.type === 'turn/start' || event.type === 'turn/end' || (isInternalTaskNotice(event) && !isRequirementDeliveryReceipt(event)))
|
|
984
1180
|
directProgress = false;
|
|
985
1181
|
if (event.type === 'user/message' && event.data.source.kind === 'user')
|
|
@@ -992,28 +1188,38 @@ export class PartnerAgentRuntime {
|
|
|
992
1188
|
if (activity)
|
|
993
1189
|
reportProgress(progress, activity);
|
|
994
1190
|
}
|
|
995
|
-
})
|
|
1191
|
+
});
|
|
996
1192
|
try {
|
|
997
1193
|
const userMessage = createUserMessage({
|
|
998
1194
|
content: inbound.content,
|
|
999
1195
|
source: { kind: 'user' },
|
|
1000
1196
|
});
|
|
1197
|
+
deliveredUserMessageId = userMessage.id;
|
|
1001
1198
|
await this.rememberMessageOrigin(session, userMessage.id);
|
|
1199
|
+
submittedAt = Date.now();
|
|
1002
1200
|
agent.followup(userMessage);
|
|
1003
1201
|
await agent.whenIdle();
|
|
1202
|
+
modelEndedAt = Date.now();
|
|
1004
1203
|
}
|
|
1005
1204
|
finally {
|
|
1006
1205
|
detach();
|
|
1007
1206
|
}
|
|
1008
1207
|
const response = channelReplyPartsAfter(agent.session.snapshotEvents(), startSeq);
|
|
1208
|
+
const stateStarted = Date.now();
|
|
1009
1209
|
await this.store.update(state => {
|
|
1010
1210
|
for (const target of state.sessions)
|
|
1011
1211
|
if (target.sessionId === session.sessionId)
|
|
1012
1212
|
target.lastMessageAt = Date.now();
|
|
1013
1213
|
});
|
|
1214
|
+
const stateEnded = Date.now();
|
|
1014
1215
|
if (deferred && deferred.length > 0)
|
|
1015
1216
|
await this.concerns?.markMentioned(companion.id, deferred.map(item => item.id)).catch(() => { });
|
|
1016
|
-
|
|
1217
|
+
const reply = await prepareChannelReply(response, session.cwd ?? this.defaultCwd);
|
|
1218
|
+
reply.postReplySource = { routeId: session.id, sessionId: session.sessionId, userMessageId: deliveredUserMessageId };
|
|
1219
|
+
if (this.store.snapshot().channels.some(c => c.id === channelId && c.platform === 'qq'))
|
|
1220
|
+
this.ctx.logger?.info?.(`[qq-latency] ${JSON.stringify({ stage: 'agent', sessionId: session.sessionId, inputPrepMs: submittedAt - enteredAt, modelMs: modelEndedAt - submittedAt, afterModelMs: Date.now() - modelEndedAt })}`);
|
|
1221
|
+
setReplyTiming(reply, { queueMs: enteredAt - queuedAt, inputPrepMs: submittedAt - enteredAt, modelAndIdleMs: modelEndedAt - submittedAt, idleAfterTurnEndMs: lastTurnEnd === undefined ? 0 : Math.max(0, modelEndedAt - lastTurnEnd), stateCommitMs: stateEnded - stateStarted, outboundPrepMs: Date.now() - stateEnded });
|
|
1222
|
+
return reply;
|
|
1017
1223
|
}
|
|
1018
1224
|
async rememberMessageOrigin(route, messageId) {
|
|
1019
1225
|
await this.store.update(state => {
|
|
@@ -1049,28 +1255,78 @@ export class PartnerAgentRuntime {
|
|
|
1049
1255
|
content.push({ type: 'text', text: '[渠道附件消息]' });
|
|
1050
1256
|
return { content, query: [message.text, ...message.attachments.map(item => item.name)].filter(Boolean).join(' ') };
|
|
1051
1257
|
}
|
|
1052
|
-
async
|
|
1258
|
+
async reconcileQqSessions() {
|
|
1259
|
+
const state = this.store.snapshot();
|
|
1260
|
+
// Startup only: workers have not started, so stale leases can be recovered.
|
|
1261
|
+
for (const route of state.sessions.filter(r => r.kind === 'channel' && state.channels.some(c => c.id === r.channelId && c.platform === 'qq'))) {
|
|
1262
|
+
const companion = state.companions.find(c => c.id === route.companionId);
|
|
1263
|
+
if (companion)
|
|
1264
|
+
await this.ensureSession(companion, route.channelId, route.userId, undefined, true);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
async ensureSession(companion, channelId, userId, title, recoverLeases = false) {
|
|
1053
1268
|
const primary = await this.ensureLocalSessionRecord(companion.id);
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1269
|
+
const snapshot = this.store.snapshot(), channel = snapshot.channels.find(c => c.id === channelId);
|
|
1270
|
+
const existing = snapshot.sessions.find(item => item.channelId === channelId && item.userId === userId && item.companionId === companion.id);
|
|
1271
|
+
const isQq = channel?.platform === 'qq', target = qqSessionTarget(userId);
|
|
1272
|
+
if (!isQq && existing?.sessionId === primary.sessionId && canReuseSession(existing, companion.id, this.ctx.workspaceRegistry.archivedSessionIds))
|
|
1056
1273
|
return existing;
|
|
1057
|
-
const
|
|
1058
|
-
const
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
userId,
|
|
1063
|
-
companionId: companion.id,
|
|
1064
|
-
sessionId: primary.sessionId,
|
|
1065
|
-
cwd: primary.cwd ?? partnerCwd(this.defaultCwd, companion.id),
|
|
1066
|
-
lastMessageAt: now,
|
|
1067
|
-
};
|
|
1068
|
-
await this.memory?.mergeScopes(companion.id, conversationMemoryScope(primary, [primary]), [memoryScope(channelId, userId)]);
|
|
1274
|
+
const sharedScope = conversationMemoryScope(primary, [primary]);
|
|
1275
|
+
const sources = isQq ? snapshot.sessions.filter(r => r.channelId === channelId && r.companionId === companion.id && qqSessionTarget(r.userId) === target && r.memoryScopeId !== sharedScope).map(r => memoryScope(r.channelId, r.userId)) : [];
|
|
1276
|
+
if (existing?.memoryScopeId !== sharedScope || sources.length)
|
|
1277
|
+
await this.memory?.mergeScopes(companion.id, sharedScope, [...new Set([...sources, memoryScope(channelId, userId)])], recoverLeases);
|
|
1278
|
+
let result;
|
|
1069
1279
|
await this.store.update(state => {
|
|
1070
|
-
|
|
1071
|
-
state.
|
|
1280
|
+
const currentChannel = state.channels.find(c => c.id === channelId);
|
|
1281
|
+
if (this.store.isCompanionRemoving(companion.id) || !state.companions.some(c => c.id === companion.id))
|
|
1282
|
+
throw Error('伙伴已移除');
|
|
1283
|
+
const main = !isQq || qqSettings(currentChannel?.qq).mainSessionPeers.includes(target);
|
|
1284
|
+
const current = state.sessions.find(r => r.channelId === channelId && r.userId === userId && r.companionId === companion.id);
|
|
1285
|
+
const peers = state.sessions.filter(r => r.channelId === channelId && r.companionId === companion.id && qqSessionTarget(r.userId) === target).sort((a, b) => b.lastMessageAt - a.lastMessageAt);
|
|
1286
|
+
const archived = this.ctx.workspaceRegistry.archivedSessionIds;
|
|
1287
|
+
// Prefer an active shared group route, then its saved separate conversation.
|
|
1288
|
+
const separate = peers.find(r => r.qqSessionMode !== 'main' && r.sessionId !== primary.sessionId && !archived.includes(r.sessionId));
|
|
1289
|
+
const saved = peers.find(r => r.qqSeparateSessionId && !archived.includes(r.qqSeparateSessionId));
|
|
1290
|
+
const separateId = separate?.sessionId ?? saved?.qqSeparateSessionId ?? `session-${randomUUID()}`;
|
|
1291
|
+
const separateCwd = separate?.cwd ?? saved?.qqSeparateCwd ?? partnerCwd(this.defaultCwd, companion.id);
|
|
1292
|
+
const sessionId = main ? primary.sessionId : separateId;
|
|
1293
|
+
const name = title?.replace(/[\x00-\x1f\x7f]/g, ' ').trim().slice(0, 140) || peers.find(r => r.qqSessionTitle)?.qqSessionTitle || `${target.startsWith('group:') ? '群聊' : '私聊'} · ${target.split(':')[1]}`;
|
|
1294
|
+
const route = { ...current, id: current?.id ?? `route-${randomUUID()}`, kind: 'channel', channelId, userId, companionId: companion.id, sessionId, cwd: main ? (primary.cwd ?? partnerCwd(this.defaultCwd, companion.id)) : separateCwd, lastMessageAt: current?.lastMessageAt ?? Date.now(),
|
|
1295
|
+
...(isQq ? { memoryScopeId: sharedScope, qqSessionTarget: target, qqSessionMode: main ? 'main' : 'separate', qqSeparateSessionId: separateId, qqSeparateCwd: separateCwd, qqSessionTitle: name } : {}) };
|
|
1296
|
+
const applied = peers.find(r => r.sessionId === sessionId && r.qqAppliedTitle)?.qqAppliedTitle;
|
|
1297
|
+
if (applied)
|
|
1298
|
+
route.qqAppliedTitle = applied;
|
|
1299
|
+
if (current?.sessionId !== sessionId)
|
|
1300
|
+
delete route.inboundMessageIds;
|
|
1301
|
+
// One group context for all members, with separate delivery/grant identities.
|
|
1302
|
+
if (isQq)
|
|
1303
|
+
for (const peer of peers) {
|
|
1304
|
+
if (peer.sessionId !== sessionId)
|
|
1305
|
+
delete peer.inboundMessageIds;
|
|
1306
|
+
Object.assign(peer, { sessionId, cwd: route.cwd, memoryScopeId: sharedScope, qqSessionTarget: target, qqSessionMode: route.qqSessionMode, qqSeparateSessionId: separateId, qqSeparateCwd: separateCwd, qqSessionTitle: name });
|
|
1307
|
+
}
|
|
1308
|
+
state.sessions = state.sessions.filter(r => !(r.channelId === channelId && r.userId === userId));
|
|
1309
|
+
state.sessions.push(route);
|
|
1310
|
+
result = route;
|
|
1072
1311
|
});
|
|
1073
|
-
return
|
|
1312
|
+
return result;
|
|
1313
|
+
}
|
|
1314
|
+
async nameQqSession(agent, route) {
|
|
1315
|
+
if (route.qqSessionMode !== 'separate' || !route.qqSessionTitle)
|
|
1316
|
+
return;
|
|
1317
|
+
const titles = this.ctx.get('sessionTitle');
|
|
1318
|
+
if (!titles)
|
|
1319
|
+
return;
|
|
1320
|
+
const current = titles.get(agent.session);
|
|
1321
|
+
// Respect a user-edited title; only refresh our own generated names.
|
|
1322
|
+
if (current?.source.kind === 'user' && current.title !== route.qqAppliedTitle)
|
|
1323
|
+
return;
|
|
1324
|
+
if (current?.title === route.qqSessionTitle)
|
|
1325
|
+
return;
|
|
1326
|
+
titles.rename(agent.session, route.qqSessionTitle);
|
|
1327
|
+
await this.store.update(state => { for (const peer of state.sessions)
|
|
1328
|
+
if (peer.sessionId === route.sessionId)
|
|
1329
|
+
peer.qqAppliedTitle = route.qqSessionTitle; });
|
|
1074
1330
|
}
|
|
1075
1331
|
async ensureAgent(companion, route) {
|
|
1076
1332
|
if (this.store.isCompanionRemoving(companion.id) || !this.store.snapshot().companions.some(item => item.id === companion.id))
|
|
@@ -1094,11 +1350,13 @@ export class PartnerAgentRuntime {
|
|
|
1094
1350
|
const held = this.handles.get(route.sessionId);
|
|
1095
1351
|
if (held !== undefined) {
|
|
1096
1352
|
await this.attachSession(companion, route);
|
|
1353
|
+
await this.nameQqSession(held.agent, route);
|
|
1097
1354
|
return held.agent;
|
|
1098
1355
|
}
|
|
1099
1356
|
const live = this.ctx.agents.get(route.sessionId);
|
|
1100
1357
|
if (live !== undefined) {
|
|
1101
1358
|
await this.attachSession(companion, route);
|
|
1359
|
+
await this.nameQqSession(live, route);
|
|
1102
1360
|
return live;
|
|
1103
1361
|
}
|
|
1104
1362
|
const options = resolveAgentOptions(this.ctx.agentDefaultModel, companion);
|
|
@@ -1134,6 +1392,7 @@ export class PartnerAgentRuntime {
|
|
|
1134
1392
|
}
|
|
1135
1393
|
this.handles.set(route.sessionId, handle);
|
|
1136
1394
|
await this.attachSession(companion, route);
|
|
1395
|
+
await this.nameQqSession(handle.agent, route);
|
|
1137
1396
|
return handle.agent;
|
|
1138
1397
|
}
|
|
1139
1398
|
async composeRestoredAgent(agent, companion, route, revision, signal) {
|
|
@@ -1157,7 +1416,7 @@ export class PartnerAgentRuntime {
|
|
|
1157
1416
|
try {
|
|
1158
1417
|
// Native DEPLOYMENT_PERSONA_SUFFIX: delivery style follows the general work preset,
|
|
1159
1418
|
// while all tool definitions and execution/permission policies remain present.
|
|
1160
|
-
disposers.push(agentCtx.systemPrompt.section({ name: 'partner-identity', order: companion.characterCard?.enabled ? 10200 : -10, interpolate: false, text: renderPartnerPersona(companion, route.kind === 'local' ? 'local' : 'conversation') }));
|
|
1419
|
+
disposers.push(agentCtx.systemPrompt.section({ name: 'partner-identity', order: companion.characterCard?.enabled ? 10200 : -10, interpolate: false, text: renderPartnerPersona(companion, route.kind === 'local' ? 'local' : 'conversation', false) }));
|
|
1161
1420
|
disposers.push(agentCtx.systemPrompt.section({ name: 'partner-tool-routing', order: -9, text: renderToolProtocol() }));
|
|
1162
1421
|
if (this.memory)
|
|
1163
1422
|
disposers.push(agentCtx.tools.register({
|
|
@@ -1371,6 +1630,8 @@ async function resolveHeartbeatFileAccess(cwd, concerns) {
|
|
|
1371
1630
|
return { root, writable };
|
|
1372
1631
|
}
|
|
1373
1632
|
export function heartbeatToolDenial(name, argumentsValue, access) {
|
|
1633
|
+
if (name === 'knowledge')
|
|
1634
|
+
return observationKnowledgeEntryDenial(argumentsValue);
|
|
1374
1635
|
if (['knowledge_note_references', 'knowledge_note_read', 'knowledge_note_update'].includes(name))
|
|
1375
1636
|
return '关注不处理依据中的笔记引用;记录仅使用手动配置的 recordTarget 和专用记录工具。';
|
|
1376
1637
|
if (!isRecord(argumentsValue))
|