@xopcai/xopc 0.0.27 → 0.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/adapters/onboard-cli.d.ts +7 -0
- package/dist/extensions/weixin/src/adapters/onboard-cli.js +61 -0
- package/dist/extensions/weixin/src/adapters/onboard-cli.js.map +1 -0
- package/dist/extensions/weixin/src/cli/qr-login.d.ts +5 -0
- package/dist/extensions/weixin/src/cli/qr-login.js +1 -1
- package/dist/extensions/weixin/src/cli/qr-login.js.map +1 -1
- package/dist/extensions/weixin/src/index.js +1 -1
- package/dist/extensions/weixin/src/plugin.d.ts +1 -0
- package/dist/extensions/weixin/src/plugin.js +2 -0
- package/dist/extensions/weixin/src/plugin.js.map +1 -1
- package/dist/gateway/static/root/assets/agents-CkgFSiCY.js +216 -0
- package/dist/gateway/static/root/assets/agents-CkgFSiCY.js.map +1 -0
- package/dist/gateway/static/root/assets/{apps-page-CBBh_Ww8.js → apps-page-Bmq19MS-.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-CBBh_Ww8.js.map → apps-page-Bmq19MS-.js.map} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js +9 -0
- package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js.map +1 -0
- package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js +2 -0
- package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js.map +1 -0
- package/dist/gateway/static/root/assets/{cron-utils-08gdQfl9.js → cron-utils-N1PqD2DB.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-08gdQfl9.js.map → cron-utils-N1PqD2DB.js.map} +1 -1
- package/dist/gateway/static/root/assets/{dist-C1MrygQH.js → dist--p2HQ2QF.js} +2 -2
- package/dist/gateway/static/root/assets/{dist-C1MrygQH.js.map → dist--p2HQ2QF.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-DN3HKUGS.js → extension-debug-page-DwHCB_6T.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-DN3HKUGS.js.map → extension-debug-page-DwHCB_6T.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CoFDHZtZ.js → extension-page-BsYwQIex.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-CoFDHZtZ.js.map → extension-page-BsYwQIex.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BcPCu_Go.js → extension-settings-page-nsisEgjB.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-BcPCu_Go.js.map → extension-settings-page-nsisEgjB.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-CR8zUHGR.js +4734 -0
- package/dist/gateway/static/root/assets/{index-PfkB8N37.js.map → index-CR8zUHGR.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-Dnfha4O2.css +1 -0
- package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js +2 -0
- package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js.map +1 -0
- package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js +2 -0
- package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js.map +1 -0
- package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js +2 -0
- package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js.map +1 -0
- package/dist/gateway/static/root/assets/skills-page-Clg8deH0.js +3 -0
- package/dist/gateway/static/root/assets/{skills-page-BmBDCEbY.js.map → skills-page-Clg8deH0.js.map} +1 -1
- package/dist/gateway/static/root/index.html +2 -2
- package/dist/package.js +1 -1
- package/dist/src/agent/lifecycle/hook-handler.d.ts +2 -0
- package/dist/src/agent/lifecycle/hook-handler.js +24 -0
- package/dist/src/agent/lifecycle/hook-handler.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.js +10 -2
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +77 -20
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.d.ts +15 -0
- package/dist/src/agent/service.js +21 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/channels/weixin/index.js +1 -1
- package/dist/src/cli/agent-chat-log-level-preset.d.ts +8 -0
- package/dist/src/cli/agent-chat-log-level-preset.js +25 -0
- package/dist/src/cli/agent-chat-log-level-preset.js.map +1 -0
- package/dist/src/cli/commands/agent/interactive.js +4 -2
- package/dist/src/cli/commands/agent/interactive.js.map +1 -1
- package/dist/src/cli/commands/agent/stream-renderer.d.ts +14 -0
- package/dist/src/cli/commands/agent/stream-renderer.js +99 -0
- package/dist/src/cli/commands/agent/stream-renderer.js.map +1 -0
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +77 -93
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/tui.d.ts +1 -0
- package/dist/src/cli/commands/tui.js +40 -0
- package/dist/src/cli/commands/tui.js.map +1 -0
- package/dist/src/cli/index.d.ts +2 -0
- package/dist/src/cli/index.js +7 -3
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/schema.d.ts +6 -0
- package/dist/src/config/schema.js +11 -3
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/extensions/hooks.js +5 -1
- package/dist/src/extensions/hooks.js.map +1 -1
- package/dist/src/extensions/loader.d.ts +1 -0
- package/dist/src/extensions/loader.js +3 -1
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/sdk/index.d.ts +1 -1
- package/dist/src/extensions/sdk/index.js.map +1 -1
- package/dist/src/extensions/types/core.d.ts +8 -0
- package/dist/src/extensions/types/hooks.d.ts +16 -1
- package/dist/src/extensions/types/hooks.js +1 -0
- package/dist/src/extensions/types/hooks.js.map +1 -1
- package/dist/src/gateway/agents-admin.d.ts +19 -1
- package/dist/src/gateway/agents-admin.js +164 -3
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/auth.d.ts +17 -3
- package/dist/src/gateway/auth.js +35 -16
- package/dist/src/gateway/auth.js.map +1 -1
- package/dist/src/gateway/hono/app.js +31 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -1
- package/dist/src/gateway/hono/middleware/auth.js +4 -3
- package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
- package/dist/src/gateway/hono/middleware/scopes.d.ts +15 -0
- package/dist/src/gateway/hono/middleware/scopes.js +41 -0
- package/dist/src/gateway/hono/middleware/scopes.js.map +1 -0
- package/dist/src/gateway/hono/routes/agents.js +59 -5
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/config.js +2 -2
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/public-gateway.js +1 -0
- package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +17 -0
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/security/audit.d.ts +18 -0
- package/dist/src/gateway/security/audit.js +68 -0
- package/dist/src/gateway/security/audit.js.map +1 -0
- package/dist/src/gateway/security/csp.d.ts +19 -0
- package/dist/src/gateway/security/csp.js +52 -0
- package/dist/src/gateway/security/csp.js.map +1 -0
- package/dist/src/gateway/security/dangerous-tools.d.ts +20 -0
- package/dist/src/gateway/security/dangerous-tools.js +46 -0
- package/dist/src/gateway/security/dangerous-tools.js.map +1 -0
- package/dist/src/gateway/security/flood-guard.d.ts +28 -0
- package/dist/src/gateway/security/flood-guard.js +42 -0
- package/dist/src/gateway/security/flood-guard.js.map +1 -0
- package/dist/src/gateway/security/index.d.ts +9 -0
- package/dist/src/gateway/security/index.js +10 -0
- package/dist/src/gateway/security/known-weak-secrets.d.ts +10 -0
- package/dist/src/gateway/security/known-weak-secrets.js +36 -0
- package/dist/src/gateway/security/known-weak-secrets.js.map +1 -0
- package/dist/src/gateway/security/operator-scopes.d.ts +37 -0
- package/dist/src/gateway/security/operator-scopes.js +137 -0
- package/dist/src/gateway/security/operator-scopes.js.map +1 -0
- package/dist/src/gateway/security/origin-check.d.ts +21 -0
- package/dist/src/gateway/security/origin-check.js +56 -0
- package/dist/src/gateway/security/origin-check.js.map +1 -0
- package/dist/src/gateway/security/preauth-connection-budget.d.ts +17 -0
- package/dist/src/gateway/security/preauth-connection-budget.js +49 -0
- package/dist/src/gateway/security/preauth-connection-budget.js.map +1 -0
- package/dist/src/gateway/security/secret-equal.d.ts +8 -0
- package/dist/src/gateway/security/secret-equal.js +30 -0
- package/dist/src/gateway/security/secret-equal.js.map +1 -0
- package/dist/src/gateway/service.d.ts +3 -1
- package/dist/src/gateway/service.js +40 -4
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/session/client-history.d.ts +21 -0
- package/dist/src/session/client-history.js +89 -0
- package/dist/src/session/client-history.js.map +1 -0
- package/dist/src/session/index.d.ts +1 -0
- package/dist/src/session/index.js +2 -1
- package/dist/src/session/manager.d.ts +2 -0
- package/dist/src/session/manager.js +5 -0
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/thinking-resolve.js +1 -1
- package/dist/src/session/thinking-resolve.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +42 -0
- package/dist/src/tui/backends/embedded-backend.js +173 -0
- package/dist/src/tui/backends/embedded-backend.js.map +1 -0
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +53 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +256 -0
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -0
- package/dist/src/tui/chat-history.d.ts +4 -0
- package/dist/src/tui/chat-history.js +29 -0
- package/dist/src/tui/chat-history.js.map +1 -0
- package/dist/src/tui/components/assistant-message.d.ts +6 -0
- package/dist/src/tui/components/assistant-message.js +19 -0
- package/dist/src/tui/components/assistant-message.js.map +1 -0
- package/dist/src/tui/components/chat-log.d.ts +21 -0
- package/dist/src/tui/components/chat-log.js +113 -0
- package/dist/src/tui/components/chat-log.js.map +1 -0
- package/dist/src/tui/components/custom-editor.d.ts +14 -0
- package/dist/src/tui/components/custom-editor.js +50 -0
- package/dist/src/tui/components/custom-editor.js.map +1 -0
- package/dist/src/tui/components/fuzzy-filter.d.ts +17 -0
- package/dist/src/tui/components/fuzzy-filter.js +85 -0
- package/dist/src/tui/components/fuzzy-filter.js.map +1 -0
- package/dist/src/tui/components/searchable-select-list.d.ts +39 -0
- package/dist/src/tui/components/searchable-select-list.js +257 -0
- package/dist/src/tui/components/searchable-select-list.js.map +1 -0
- package/dist/src/tui/components/tool-execution.d.ts +16 -0
- package/dist/src/tui/components/tool-execution.js +76 -0
- package/dist/src/tui/components/tool-execution.js.map +1 -0
- package/dist/src/tui/components/user-message.d.ts +6 -0
- package/dist/src/tui/components/user-message.js +22 -0
- package/dist/src/tui/components/user-message.js.map +1 -0
- package/dist/src/tui/sse-consumer.d.ts +15 -0
- package/dist/src/tui/sse-consumer.js +75 -0
- package/dist/src/tui/sse-consumer.js.map +1 -0
- package/dist/src/tui/stream-assembler.d.ts +22 -0
- package/dist/src/tui/stream-assembler.js +63 -0
- package/dist/src/tui/stream-assembler.js.map +1 -0
- package/dist/src/tui/theme.d.ts +73 -0
- package/dist/src/tui/theme.js +157 -0
- package/dist/src/tui/theme.js.map +1 -0
- package/dist/src/tui/tui-agent-events.d.ts +7 -0
- package/dist/src/tui/tui-agent-events.js +103 -0
- package/dist/src/tui/tui-agent-events.js.map +1 -0
- package/dist/src/tui/tui-backend.d.ts +80 -0
- package/dist/src/tui/tui-backend.js +1 -0
- package/dist/src/tui/tui-commands.d.ts +23 -0
- package/dist/src/tui/tui-commands.js +165 -0
- package/dist/src/tui/tui-commands.js.map +1 -0
- package/dist/src/tui/tui-lifecycle.d.ts +26 -0
- package/dist/src/tui/tui-lifecycle.js +57 -0
- package/dist/src/tui/tui-lifecycle.js.map +1 -0
- package/dist/src/tui/tui-local-shell.d.ts +28 -0
- package/dist/src/tui/tui-local-shell.js +147 -0
- package/dist/src/tui/tui-local-shell.js.map +1 -0
- package/dist/src/tui/tui-overlays.d.ts +8 -0
- package/dist/src/tui/tui-overlays.js +22 -0
- package/dist/src/tui/tui-overlays.js.map +1 -0
- package/dist/src/tui/tui-picker-overlay.d.ts +26 -0
- package/dist/src/tui/tui-picker-overlay.js +69 -0
- package/dist/src/tui/tui-picker-overlay.js.map +1 -0
- package/dist/src/tui/tui-stdio-filter.d.ts +17 -0
- package/dist/src/tui/tui-stdio-filter.js +96 -0
- package/dist/src/tui/tui-stdio-filter.js.map +1 -0
- package/dist/src/tui/tui-submit.d.ts +25 -0
- package/dist/src/tui/tui-submit.js +102 -0
- package/dist/src/tui/tui-submit.js.map +1 -0
- package/dist/src/tui/tui-suspend.d.ts +10 -0
- package/dist/src/tui/tui-suspend.js +18 -0
- package/dist/src/tui/tui-suspend.js.map +1 -0
- package/dist/src/tui/tui-types.d.ts +86 -0
- package/dist/src/tui/tui-types.js +21 -0
- package/dist/src/tui/tui-types.js.map +1 -0
- package/dist/src/tui/tui.d.ts +5 -0
- package/dist/src/tui/tui.js +389 -0
- package/dist/src/tui/tui.js.map +1 -0
- package/package.json +5 -3
- package/dist/gateway/static/root/assets/agents-w8_jzuiX.js +0 -216
- package/dist/gateway/static/root/assets/agents-w8_jzuiX.js.map +0 -1
- package/dist/gateway/static/root/assets/channels-settings-DUKRPC7C.js +0 -9
- package/dist/gateway/static/root/assets/channels-settings-DUKRPC7C.js.map +0 -1
- package/dist/gateway/static/root/assets/cron-page-S18t1yG-.js +0 -2
- package/dist/gateway/static/root/assets/cron-page-S18t1yG-.js.map +0 -1
- package/dist/gateway/static/root/assets/index-OT4cGzon.css +0 -1
- package/dist/gateway/static/root/assets/index-PfkB8N37.js +0 -4734
- package/dist/gateway/static/root/assets/logs-page-DoWe1GWy.js +0 -2
- package/dist/gateway/static/root/assets/logs-page-DoWe1GWy.js.map +0 -1
- package/dist/gateway/static/root/assets/sessions-page-2uOYwEwd.js +0 -2
- package/dist/gateway/static/root/assets/sessions-page-2uOYwEwd.js.map +0 -1
- package/dist/gateway/static/root/assets/settings-page-fQWswCuq.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-fQWswCuq.js.map +0 -1
- package/dist/gateway/static/root/assets/skills-page-BmBDCEbY.js +0 -3
|
@@ -8,6 +8,10 @@ import "../../chat-commands/index.js";
|
|
|
8
8
|
init_providers();
|
|
9
9
|
init_logger();
|
|
10
10
|
const log = createLogger("CommandHandler");
|
|
11
|
+
/** Gateway console direct stream uses SSE tokens; there is no ChannelPlugin outbound for `webchat`. */
|
|
12
|
+
function shouldSkipBusOutboundForChannel(channel) {
|
|
13
|
+
return channel === "webchat";
|
|
14
|
+
}
|
|
11
15
|
var CommandHandler = class {
|
|
12
16
|
config;
|
|
13
17
|
bus;
|
|
@@ -61,6 +65,7 @@ var CommandHandler = class {
|
|
|
61
65
|
sessionConfigStore: this.sessionConfigStore,
|
|
62
66
|
applySessionThinkingLevel: this.applySessionThinkingLevel,
|
|
63
67
|
replyHandler: async (text, _options) => {
|
|
68
|
+
if (shouldSkipBusOutboundForChannel(context.channel)) return;
|
|
64
69
|
await this.bus.publishOutbound({
|
|
65
70
|
channel: context.channel,
|
|
66
71
|
chat_id: context.chatId,
|
|
@@ -69,6 +74,7 @@ var CommandHandler = class {
|
|
|
69
74
|
});
|
|
70
75
|
},
|
|
71
76
|
typingHandler: async (typing) => {
|
|
77
|
+
if (shouldSkipBusOutboundForChannel(context.channel)) return;
|
|
72
78
|
await this.bus.publishOutbound({
|
|
73
79
|
channel: context.channel,
|
|
74
80
|
chat_id: context.chatId,
|
|
@@ -118,7 +124,7 @@ var CommandHandler = class {
|
|
|
118
124
|
getSessionContextReport: this.getSessionContextReport
|
|
119
125
|
});
|
|
120
126
|
const result = await commandRegistry.execute(commandName, cmdCtx, args);
|
|
121
|
-
if (result.content) await this.bus.publishOutbound({
|
|
127
|
+
if (result.content && !shouldSkipBusOutboundForChannel(context.channel)) await this.bus.publishOutbound({
|
|
122
128
|
channel: context.channel,
|
|
123
129
|
chat_id: context.chatId,
|
|
124
130
|
content: result.content,
|
|
@@ -161,6 +167,7 @@ var CommandHandler = class {
|
|
|
161
167
|
applySessionThinkingLevel: this.applySessionThinkingLevel,
|
|
162
168
|
replyHandler: async (text, _options) => {
|
|
163
169
|
segments.push(text);
|
|
170
|
+
if (shouldSkipBusOutboundForChannel(context.channel)) return;
|
|
164
171
|
await this.bus.publishOutbound({
|
|
165
172
|
channel: context.channel,
|
|
166
173
|
chat_id: context.chatId,
|
|
@@ -169,6 +176,7 @@ var CommandHandler = class {
|
|
|
169
176
|
});
|
|
170
177
|
},
|
|
171
178
|
typingHandler: async (typing) => {
|
|
179
|
+
if (shouldSkipBusOutboundForChannel(context.channel)) return;
|
|
172
180
|
await this.bus.publishOutbound({
|
|
173
181
|
channel: context.channel,
|
|
174
182
|
chat_id: context.chatId,
|
|
@@ -220,7 +228,7 @@ var CommandHandler = class {
|
|
|
220
228
|
const result = await commandRegistry.execute(commandName, wrapped, args);
|
|
221
229
|
if (result.content) {
|
|
222
230
|
segments.push(result.content);
|
|
223
|
-
await this.bus.publishOutbound({
|
|
231
|
+
if (!shouldSkipBusOutboundForChannel(context.channel)) await this.bus.publishOutbound({
|
|
224
232
|
channel: context.channel,
|
|
225
233
|
chat_id: context.chatId,
|
|
226
234
|
content: result.content,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-handler.js","names":[],"sources":["../../../../src/agent/messaging/command-handler.ts"],"sourcesContent":["/**\n * Command Handler - Parses and executes commands\n *\n * Handles command execution using the unified command system.\n */\n\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport type { Config } from '../../config/schema.js';\nimport { isProviderConfiguredSync } from '../../providers/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { CompactionResult } from '../memory/compaction.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { commandRegistry, createCommandContext } from '../../chat-commands/index.js';\nimport { getAllProviders, getModelsByProvider, getProviderDisplayName } from '../../providers/index.js';\n\nconst log = createLogger('CommandHandler');\n\nexport interface CommandContext {\n sessionKey: string;\n channel: string;\n chatId: string;\n senderId: string;\n isGroup: boolean;\n}\n\nexport interface CommandHandlerConfig {\n config: Config;\n bus: MessageBus;\n sessionStore: SessionStore;\n sessionConfigStore?: SessionConfigStore;\n /** After /think persists, sync pi-agent */\n applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n getCurrentModel: () => string;\n switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n /** Drop in-memory agent after session file is cleared (e.g. /new) */\n invalidateAgentSession?: (sessionKey: string) => void;\n /** Cancel streaming preview + in-flight LLM work for this session (e.g. /abort) */\n abortSessionTurn?: (sessionKey: string) => Promise<void>;\n\n compactSession?: (\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ) => Promise<CompactionResult>;\n\n btwQuery?: (sessionKey: string, question: string) => Promise<{ text: string; error?: string }>;\n\n getSessionContextReport?: (\n sessionKey: string,\n mode: 'list' | 'detail' | 'json',\n ) => Promise<string>;\n}\n\nexport class CommandHandler {\n private config: Config;\n private bus: MessageBus;\n private sessionStore: SessionStore;\n private sessionConfigStore?: SessionConfigStore;\n private applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n private getCurrentModel: () => string;\n private switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n private invalidateAgentSession?: (sessionKey: string) => void;\n private abortSessionTurn?: (sessionKey: string) => Promise<void>;\n private compactSession?: CommandHandlerConfig['compactSession'];\n private btwQuery?: CommandHandlerConfig['btwQuery'];\n private getSessionContextReport?: CommandHandlerConfig['getSessionContextReport'];\n\n constructor(handlerConfig: CommandHandlerConfig) {\n this.config = handlerConfig.config;\n this.bus = handlerConfig.bus;\n this.sessionStore = handlerConfig.sessionStore;\n this.sessionConfigStore = handlerConfig.sessionConfigStore;\n this.applySessionThinkingLevel = handlerConfig.applySessionThinkingLevel;\n this.getCurrentModel = handlerConfig.getCurrentModel;\n this.switchModelForSession = handlerConfig.switchModelForSession;\n this.invalidateAgentSession = handlerConfig.invalidateAgentSession;\n this.abortSessionTurn = handlerConfig.abortSessionTurn;\n this.compactSession = handlerConfig.compactSession;\n this.btwQuery = handlerConfig.btwQuery;\n this.getSessionContextReport = handlerConfig.getSessionContextReport;\n }\n\n /** Replace config reference after hot reload or gateway PATCH so commands see current defaults. */\n updateAgentConfig(config: Config): void {\n this.config = config;\n }\n\n /**\n * Execute a command using the unified command system\n */\n async executeCommand(\n commandName: string,\n args: string,\n context: CommandContext\n ): Promise<boolean> {\n // Check if command exists\n if (!commandRegistry.has(commandName)) {\n return false;\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command via new system');\n\n // Create command context\n const cmdCtx = createCommandContext({\n sessionKey: context.sessionKey,\n source: context.channel as 'telegram' | 'webui' | 'cli' | 'api' | 'system' | 'gateway',\n channelId: context.channel,\n chatId: context.chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n config: this.config,\n bus: this.bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: this.applySessionThinkingLevel,\n\n replyHandler: async (text: string, _options?) => {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: text,\n type: 'message',\n });\n },\n\n typingHandler: async (typing: boolean) => {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n type: typing ? 'typing_on' : 'typing_off',\n });\n },\n\n supportedFeatures: ['markdown', 'typing'],\n\n getCurrentModel: this.getCurrentModel,\n\n switchModel: async (modelId: string) => {\n return this.switchModelForSession(context.sessionKey, modelId);\n },\n\n listModels: async () => {\n const providers = getAllProviders();\n const models: Array<{ id: string; name: string; provider: string }> = [];\n\n for (const providerId of providers) {\n if (isProviderConfiguredSync(providerId)) {\n const providerModels = getModelsByProvider(providerId);\n for (const m of providerModels) {\n models.push({\n id: `${m.provider}/${m.id}`,\n name: m.name || m.id,\n provider: getProviderDisplayName(providerId),\n });\n }\n }\n }\n\n return models;\n },\n\n getUsage: async () => {\n const messages = await this.sessionStore.load(context.sessionKey);\n let promptTokens = 0;\n let completionTokens = 0;\n\n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n const usage = msg.usage as any;\n promptTokens += usage.input || 0;\n completionTokens += usage.output || 0;\n }\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n },\n\n invalidateAgentSession: this.invalidateAgentSession,\n\n abortCurrentTurn: this.abortSessionTurn\n ? async () => {\n await this.abortSessionTurn!(context.sessionKey);\n }\n : undefined,\n\n compactSession: this.compactSession,\n btwQuery: this.btwQuery,\n getSessionContextReport: this.getSessionContextReport,\n });\n\n const result = await commandRegistry.execute(commandName, cmdCtx, args);\n\n if (result.content) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n\n return true;\n }\n\n /**\n * Run command and return all user-visible text (ctx.reply + result.content) for SSE/CLI.\n * Same bus side effects as {@link executeCommand}.\n */\n async executeCommandAndAggregateReply(\n commandName: string,\n args: string,\n context: CommandContext,\n ): Promise<{ handled: boolean; aggregatedText: string }> {\n if (!commandRegistry.has(commandName)) {\n return { handled: false, aggregatedText: '' };\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command (aggregate reply)');\n\n const { aggregatedText } = await this.runRegistryExecuteWithCapture(args, commandName, context);\n\n return { handled: true, aggregatedText };\n }\n\n private async runRegistryExecuteWithCapture(\n args: string,\n commandName: string,\n context: CommandContext,\n ): Promise<{ aggregatedText: string }> {\n const segments: string[] = [];\n\n const wrapped = createCommandContext({\n sessionKey: context.sessionKey,\n source: context.channel as 'telegram' | 'webui' | 'cli' | 'api' | 'system' | 'gateway',\n channelId: context.channel,\n chatId: context.chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n config: this.config,\n bus: this.bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: this.applySessionThinkingLevel,\n\n replyHandler: async (text: string, _options?) => {\n segments.push(text);\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: text,\n type: 'message',\n });\n },\n\n typingHandler: async (typing: boolean) => {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n type: typing ? 'typing_on' : 'typing_off',\n });\n },\n\n supportedFeatures: ['markdown', 'typing'],\n\n getCurrentModel: this.getCurrentModel,\n\n switchModel: async (modelId: string) => {\n return this.switchModelForSession(context.sessionKey, modelId);\n },\n\n listModels: async () => {\n const providers = getAllProviders();\n const models: Array<{ id: string; name: string; provider: string }> = [];\n\n for (const providerId of providers) {\n if (isProviderConfiguredSync(providerId)) {\n const providerModels = getModelsByProvider(providerId);\n for (const m of providerModels) {\n models.push({\n id: `${m.provider}/${m.id}`,\n name: m.name || m.id,\n provider: getProviderDisplayName(providerId),\n });\n }\n }\n }\n\n return models;\n },\n\n getUsage: async () => {\n const messages = await this.sessionStore.load(context.sessionKey);\n let promptTokens = 0;\n let completionTokens = 0;\n\n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n const usage = msg.usage as { input?: number; output?: number };\n promptTokens += usage.input || 0;\n completionTokens += usage.output || 0;\n }\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n },\n\n invalidateAgentSession: this.invalidateAgentSession,\n\n abortCurrentTurn: this.abortSessionTurn\n ? async () => {\n await this.abortSessionTurn!(context.sessionKey);\n }\n : undefined,\n\n compactSession: this.compactSession,\n btwQuery: this.btwQuery,\n getSessionContextReport: this.getSessionContextReport,\n });\n\n const result = await commandRegistry.execute(commandName, wrapped, args);\n\n if (result.content) {\n segments.push(result.content);\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n\n const aggregatedText = segments.filter((s) => s && s.trim()).join('\\n\\n');\n return { aggregatedText };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"command-handler.js","names":[],"sources":["../../../../src/agent/messaging/command-handler.ts"],"sourcesContent":["/**\n * Command Handler - Parses and executes commands\n *\n * Handles command execution using the unified command system.\n */\n\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport type { Config } from '../../config/schema.js';\nimport { isProviderConfiguredSync } from '../../providers/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { CompactionResult } from '../memory/compaction.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { commandRegistry, createCommandContext } from '../../chat-commands/index.js';\nimport { getAllProviders, getModelsByProvider, getProviderDisplayName } from '../../providers/index.js';\n\nconst log = createLogger('CommandHandler');\n\n/** Gateway console direct stream uses SSE tokens; there is no ChannelPlugin outbound for `webchat`. */\nfunction shouldSkipBusOutboundForChannel(channel: string): boolean {\n return channel === 'webchat';\n}\n\nexport interface CommandContext {\n sessionKey: string;\n channel: string;\n chatId: string;\n senderId: string;\n isGroup: boolean;\n}\n\nexport interface CommandHandlerConfig {\n config: Config;\n bus: MessageBus;\n sessionStore: SessionStore;\n sessionConfigStore?: SessionConfigStore;\n /** After /think persists, sync pi-agent */\n applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n getCurrentModel: () => string;\n switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n /** Drop in-memory agent after session file is cleared (e.g. /new) */\n invalidateAgentSession?: (sessionKey: string) => void;\n /** Cancel streaming preview + in-flight LLM work for this session (e.g. /abort) */\n abortSessionTurn?: (sessionKey: string) => Promise<void>;\n\n compactSession?: (\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ) => Promise<CompactionResult>;\n\n btwQuery?: (sessionKey: string, question: string) => Promise<{ text: string; error?: string }>;\n\n getSessionContextReport?: (\n sessionKey: string,\n mode: 'list' | 'detail' | 'json',\n ) => Promise<string>;\n}\n\nexport class CommandHandler {\n private config: Config;\n private bus: MessageBus;\n private sessionStore: SessionStore;\n private sessionConfigStore?: SessionConfigStore;\n private applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n private getCurrentModel: () => string;\n private switchModelForSession: (sessionKey: string, modelId: string) => Promise<boolean>;\n private invalidateAgentSession?: (sessionKey: string) => void;\n private abortSessionTurn?: (sessionKey: string) => Promise<void>;\n private compactSession?: CommandHandlerConfig['compactSession'];\n private btwQuery?: CommandHandlerConfig['btwQuery'];\n private getSessionContextReport?: CommandHandlerConfig['getSessionContextReport'];\n\n constructor(handlerConfig: CommandHandlerConfig) {\n this.config = handlerConfig.config;\n this.bus = handlerConfig.bus;\n this.sessionStore = handlerConfig.sessionStore;\n this.sessionConfigStore = handlerConfig.sessionConfigStore;\n this.applySessionThinkingLevel = handlerConfig.applySessionThinkingLevel;\n this.getCurrentModel = handlerConfig.getCurrentModel;\n this.switchModelForSession = handlerConfig.switchModelForSession;\n this.invalidateAgentSession = handlerConfig.invalidateAgentSession;\n this.abortSessionTurn = handlerConfig.abortSessionTurn;\n this.compactSession = handlerConfig.compactSession;\n this.btwQuery = handlerConfig.btwQuery;\n this.getSessionContextReport = handlerConfig.getSessionContextReport;\n }\n\n /** Replace config reference after hot reload or gateway PATCH so commands see current defaults. */\n updateAgentConfig(config: Config): void {\n this.config = config;\n }\n\n /**\n * Execute a command using the unified command system\n */\n async executeCommand(\n commandName: string,\n args: string,\n context: CommandContext\n ): Promise<boolean> {\n // Check if command exists\n if (!commandRegistry.has(commandName)) {\n return false;\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command via new system');\n\n // Create command context\n const cmdCtx = createCommandContext({\n sessionKey: context.sessionKey,\n source: context.channel as 'telegram' | 'webui' | 'cli' | 'api' | 'system' | 'gateway',\n channelId: context.channel,\n chatId: context.chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n config: this.config,\n bus: this.bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: this.applySessionThinkingLevel,\n\n replyHandler: async (text: string, _options?) => {\n if (shouldSkipBusOutboundForChannel(context.channel)) return;\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: text,\n type: 'message',\n });\n },\n\n typingHandler: async (typing: boolean) => {\n if (shouldSkipBusOutboundForChannel(context.channel)) return;\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n type: typing ? 'typing_on' : 'typing_off',\n });\n },\n\n supportedFeatures: ['markdown', 'typing'],\n\n getCurrentModel: this.getCurrentModel,\n\n switchModel: async (modelId: string) => {\n return this.switchModelForSession(context.sessionKey, modelId);\n },\n\n listModels: async () => {\n const providers = getAllProviders();\n const models: Array<{ id: string; name: string; provider: string }> = [];\n\n for (const providerId of providers) {\n if (isProviderConfiguredSync(providerId)) {\n const providerModels = getModelsByProvider(providerId);\n for (const m of providerModels) {\n models.push({\n id: `${m.provider}/${m.id}`,\n name: m.name || m.id,\n provider: getProviderDisplayName(providerId),\n });\n }\n }\n }\n\n return models;\n },\n\n getUsage: async () => {\n const messages = await this.sessionStore.load(context.sessionKey);\n let promptTokens = 0;\n let completionTokens = 0;\n\n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n const usage = msg.usage as any;\n promptTokens += usage.input || 0;\n completionTokens += usage.output || 0;\n }\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n },\n\n invalidateAgentSession: this.invalidateAgentSession,\n\n abortCurrentTurn: this.abortSessionTurn\n ? async () => {\n await this.abortSessionTurn!(context.sessionKey);\n }\n : undefined,\n\n compactSession: this.compactSession,\n btwQuery: this.btwQuery,\n getSessionContextReport: this.getSessionContextReport,\n });\n\n const result = await commandRegistry.execute(commandName, cmdCtx, args);\n\n if (result.content && !shouldSkipBusOutboundForChannel(context.channel)) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n\n return true;\n }\n\n /**\n * Run command and return all user-visible text (ctx.reply + result.content) for SSE/CLI.\n * Same bus side effects as {@link executeCommand}.\n */\n async executeCommandAndAggregateReply(\n commandName: string,\n args: string,\n context: CommandContext,\n ): Promise<{ handled: boolean; aggregatedText: string }> {\n if (!commandRegistry.has(commandName)) {\n return { handled: false, aggregatedText: '' };\n }\n\n log.info({ command: commandName, sessionKey: context.sessionKey }, 'Executing command (aggregate reply)');\n\n const { aggregatedText } = await this.runRegistryExecuteWithCapture(args, commandName, context);\n\n return { handled: true, aggregatedText };\n }\n\n private async runRegistryExecuteWithCapture(\n args: string,\n commandName: string,\n context: CommandContext,\n ): Promise<{ aggregatedText: string }> {\n const segments: string[] = [];\n\n const wrapped = createCommandContext({\n sessionKey: context.sessionKey,\n source: context.channel as 'telegram' | 'webui' | 'cli' | 'api' | 'system' | 'gateway',\n channelId: context.channel,\n chatId: context.chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n config: this.config,\n bus: this.bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: this.applySessionThinkingLevel,\n\n replyHandler: async (text: string, _options?) => {\n segments.push(text);\n if (shouldSkipBusOutboundForChannel(context.channel)) return;\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: text,\n type: 'message',\n });\n },\n\n typingHandler: async (typing: boolean) => {\n if (shouldSkipBusOutboundForChannel(context.channel)) return;\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n type: typing ? 'typing_on' : 'typing_off',\n });\n },\n\n supportedFeatures: ['markdown', 'typing'],\n\n getCurrentModel: this.getCurrentModel,\n\n switchModel: async (modelId: string) => {\n return this.switchModelForSession(context.sessionKey, modelId);\n },\n\n listModels: async () => {\n const providers = getAllProviders();\n const models: Array<{ id: string; name: string; provider: string }> = [];\n\n for (const providerId of providers) {\n if (isProviderConfiguredSync(providerId)) {\n const providerModels = getModelsByProvider(providerId);\n for (const m of providerModels) {\n models.push({\n id: `${m.provider}/${m.id}`,\n name: m.name || m.id,\n provider: getProviderDisplayName(providerId),\n });\n }\n }\n }\n\n return models;\n },\n\n getUsage: async () => {\n const messages = await this.sessionStore.load(context.sessionKey);\n let promptTokens = 0;\n let completionTokens = 0;\n\n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n const usage = msg.usage as { input?: number; output?: number };\n promptTokens += usage.input || 0;\n completionTokens += usage.output || 0;\n }\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n },\n\n invalidateAgentSession: this.invalidateAgentSession,\n\n abortCurrentTurn: this.abortSessionTurn\n ? async () => {\n await this.abortSessionTurn!(context.sessionKey);\n }\n : undefined,\n\n compactSession: this.compactSession,\n btwQuery: this.btwQuery,\n getSessionContextReport: this.getSessionContextReport,\n });\n\n const result = await commandRegistry.execute(commandName, wrapped, args);\n\n if (result.content) {\n segments.push(result.content);\n if (!shouldSkipBusOutboundForChannel(context.channel)) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: result.content,\n type: 'message',\n });\n }\n }\n\n const aggregatedText = segments.filter((s) => s && s.trim()).join('\\n\\n');\n return { aggregatedText };\n }\n}\n"],"mappings":"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"}
|
|
@@ -20,7 +20,7 @@ async function* runProcessDirectStreaming(deps, input) {
|
|
|
20
20
|
let agentDone = false;
|
|
21
21
|
let lastSentContent = "";
|
|
22
22
|
let lastSentThinking = "";
|
|
23
|
-
let reasoningLevel = "
|
|
23
|
+
let reasoningLevel = "stream";
|
|
24
24
|
const enqueueSseEvent = (event) => {
|
|
25
25
|
eventQueue.push(event);
|
|
26
26
|
if (resolveWaiting) {
|
|
@@ -44,18 +44,22 @@ async function* runProcessDirectStreaming(deps, input) {
|
|
|
44
44
|
switch (event.type) {
|
|
45
45
|
case "tool_execution_start": {
|
|
46
46
|
const toolEvent = event;
|
|
47
|
+
const toolName = typeof toolEvent.toolName === "string" && toolEvent.toolName.trim() ? toolEvent.toolName.trim() : "unknown";
|
|
47
48
|
pushEvent({
|
|
48
49
|
type: "tool_start",
|
|
49
|
-
|
|
50
|
+
toolCallId: toolEvent.toolCallId,
|
|
51
|
+
toolName,
|
|
50
52
|
args: toolEvent.args
|
|
51
53
|
});
|
|
52
54
|
break;
|
|
53
55
|
}
|
|
54
56
|
case "tool_execution_end": {
|
|
55
57
|
const toolEvent = event;
|
|
58
|
+
const toolName = typeof toolEvent.toolName === "string" && toolEvent.toolName.trim() ? toolEvent.toolName.trim() : "unknown";
|
|
56
59
|
pushEvent({
|
|
57
60
|
type: "tool_end",
|
|
58
|
-
|
|
61
|
+
toolCallId: toolEvent.toolCallId,
|
|
62
|
+
toolName,
|
|
59
63
|
isError: toolEvent.isError,
|
|
60
64
|
result: serializeAgentToolResultForSse(toolEvent.result)
|
|
61
65
|
});
|
|
@@ -145,7 +149,7 @@ async function* runProcessDirectStreaming(deps, input) {
|
|
|
145
149
|
agent.state.messages = prepareLoadedSessionMessages(sessionKey, loaded);
|
|
146
150
|
await modelManager.applyModelForSession(agent, sessionKey);
|
|
147
151
|
await applyResolvedThinkingLevel(sessionKey, input.thinking);
|
|
148
|
-
reasoningLevel = await resolveEffectiveReasoningLevel(sessionConfigStore, sessionKey, getConfig()?.agents?.defaults?.reasoningDefault ?? "
|
|
152
|
+
reasoningLevel = await resolveEffectiveReasoningLevel(sessionConfigStore, sessionKey, getConfig()?.agents?.defaults?.reasoningDefault ?? "stream");
|
|
149
153
|
const sttCfg = mergeSttConfigFromAppConfig(getConfig()?.stt);
|
|
150
154
|
const { text: mergedUserText, inboundVoice } = await mergeVoiceTranscriptsIntoUserText(attachmentRootsForSession(sessionKey), prepared, input.content, sttCfg);
|
|
151
155
|
const armAbort = () => {
|
|
@@ -160,21 +164,57 @@ async function* runProcessDirectStreaming(deps, input) {
|
|
|
160
164
|
else signal.addEventListener("abort", armAbort, { once: true });
|
|
161
165
|
const commandInfo = parseSlashCommand(mergedUserText);
|
|
162
166
|
let ranSlashCommand = false;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
167
|
+
/** Plain text to persist for webchat slash turns (SSE can be dropped by the UI; disk is source of truth). */
|
|
168
|
+
let webchatSlashReceipt;
|
|
169
|
+
if (!abortHandled && commandInfo) {
|
|
170
|
+
if (commandRegistry.has(commandInfo.command)) {
|
|
171
|
+
ranSlashCommand = true;
|
|
172
|
+
try {
|
|
173
|
+
const { aggregatedText } = await commandHandler.executeCommandAndAggregateReply(commandInfo.command, commandInfo.args, {
|
|
174
|
+
sessionKey,
|
|
175
|
+
channel,
|
|
176
|
+
chatId,
|
|
177
|
+
senderId: context.senderId,
|
|
178
|
+
isGroup: context.isGroup
|
|
179
|
+
});
|
|
180
|
+
if (aggregatedText?.trim()) {
|
|
181
|
+
webchatSlashReceipt = aggregatedText.trim();
|
|
182
|
+
pushEvent({
|
|
183
|
+
type: "token",
|
|
184
|
+
content: webchatSlashReceipt
|
|
185
|
+
});
|
|
186
|
+
} else if (channel === "webchat") {
|
|
187
|
+
webchatSlashReceipt = "Command finished with no assistant text. For `/goal`, that usually means the goal was saved; the assistant will not run until your next normal message (or an auto-continuation after an assistant reply).";
|
|
188
|
+
pushEvent({
|
|
189
|
+
type: "token",
|
|
190
|
+
content: webchatSlashReceipt
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
} catch (cmdErr) {
|
|
194
|
+
const em = cmdErr instanceof Error ? cmdErr.message : String(cmdErr);
|
|
195
|
+
log.warn({
|
|
196
|
+
err: cmdErr,
|
|
197
|
+
sessionKey,
|
|
198
|
+
command: commandInfo.command
|
|
199
|
+
}, `Slash command failed: ${em}`);
|
|
200
|
+
webchatSlashReceipt = `Command error: ${em}`;
|
|
201
|
+
pushEvent({
|
|
202
|
+
type: "token",
|
|
203
|
+
content: webchatSlashReceipt
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
pushEvent({ type: "__done__" });
|
|
207
|
+
agentDone = true;
|
|
208
|
+
} else if (commandInfo.command === "goal") {
|
|
209
|
+
ranSlashCommand = true;
|
|
210
|
+
webchatSlashReceipt = "The /goal command is not available: the `standing-goal` extension is not loaded.\n\nFix: add `\"standing-goal\"` to `extensions.enabled` in your xopc config (or remove it from `extensions.disabled`), then restart the gateway. If you use a strict allowlist, include `standing-goal` in `extensions.enabled`.";
|
|
211
|
+
pushEvent({
|
|
212
|
+
type: "token",
|
|
213
|
+
content: webchatSlashReceipt
|
|
214
|
+
});
|
|
215
|
+
pushEvent({ type: "__done__" });
|
|
216
|
+
agentDone = true;
|
|
217
|
+
}
|
|
178
218
|
}
|
|
179
219
|
let messageContent;
|
|
180
220
|
if (!abortHandled && !ranSlashCommand) messageContent = await buildMessageContent(mergedUserText.trimStart().startsWith("/skill:") ? agentManager.expandSkillUserText(mergedUserText) : mergedUserText, prepared, sessionKey);
|
|
@@ -222,7 +262,24 @@ async function* runProcessDirectStreaming(deps, input) {
|
|
|
222
262
|
if (event.type === "__done__") continue;
|
|
223
263
|
yield event;
|
|
224
264
|
}
|
|
225
|
-
|
|
265
|
+
if (channel === "webchat" && ranSlashCommand && webchatSlashReceipt?.trim()) try {
|
|
266
|
+
const loaded = await sessionStore.load(sessionKey);
|
|
267
|
+
const assistantMsg = {
|
|
268
|
+
role: "assistant",
|
|
269
|
+
content: [{
|
|
270
|
+
type: "text",
|
|
271
|
+
text: webchatSlashReceipt.trim()
|
|
272
|
+
}],
|
|
273
|
+
timestamp: Date.now()
|
|
274
|
+
};
|
|
275
|
+
await sessionStore.save(sessionKey, [...loaded, assistantMsg]);
|
|
276
|
+
} catch (err) {
|
|
277
|
+
log.warn({
|
|
278
|
+
err,
|
|
279
|
+
sessionKey
|
|
280
|
+
}, "Failed to persist webchat slash command receipt");
|
|
281
|
+
}
|
|
282
|
+
if (!ranSlashCommand) await persistAgentSessionMessages(sessionKey);
|
|
226
283
|
if (!userAborted) {
|
|
227
284
|
const ttsAudioEvent = await maybeEmitWebchatTts(sessionKey, inboundVoice);
|
|
228
285
|
if (ttsAudioEvent) yield ttsAudioEvent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-direct-streaming.js","names":["msgEvent"],"sources":["../../../../src/agent/service/process-direct-streaming.ts"],"sourcesContent":["import type { AgentEvent, AgentMessage } from '@mariozechner/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport type { InternalAttachmentRoots } from '../../channels/attachments/inbound-persist.js';\nimport { commandRegistry } from '../../chat-commands/index.js';\nimport { parseSlashCommand } from '../../chat-commands/command-parse.js';\nimport {\n mergeVoiceTranscriptsIntoUserText,\n mergeSttConfigFromAppConfig,\n} from '../../channels/attachments/voice-stt-webchat.js';\nimport {\n resolveEffectiveReasoningLevel,\n type SessionConfigStore,\n type SessionStore,\n} from '../../session/index.js';\nimport type { SessionContext } from '../session/index.js';\nimport {\n extractTextContent,\n extractThinkingContent,\n extractThinkingFromAssistantMessage,\n} from '../context/workspace.js';\nimport { extractAgentUserPlainText } from '../memory/user-message-text.js';\nimport { runAgentTurnWithModelFallbacks } from '../orchestration/run-agent-turn-with-fallbacks.js';\nimport { applyReasoningVisibilityToSseEvent } from '../streaming/reasoning-visibility-sse.js';\nimport type { ReasoningLevel } from '../transcript/thinking-types.js';\nimport { serializeAgentToolResultForSse } from '../service-inbound-utils.js';\nimport type { AgentManager } from '../agent-manager.js';\nimport type { AgentEventHandler } from '../orchestration/agent-event-handler.js';\nimport type { AgentOrchestrator } from '../orchestration/agent-orchestrator.js';\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { ModelManager } from '../models/index.js';\n\nexport type DirectStreamInboundAttachment = {\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n};\n\n/** Logger shape expected by {@link runAgentTurnWithModelFallbacks}. */\nexport type ProcessDirectStreamLog = {\n info: (obj: Record<string, unknown>, msg: string) => void;\n warn: (obj: Record<string, unknown>, msg: string) => void;\n debug?: (obj: Record<string, unknown>, msg: string) => void;\n};\n\n/**\n * Explicit collaborators for {@link runProcessDirectStreaming} (gateway webchat / CLI direct stream).\n * Bound by `AgentService` from its private fields and methods.\n */\nexport interface ProcessDirectStreamingDeps {\n log: ProcessDirectStreamLog;\n parseSessionKey: (sessionKey: string) => { channel: string; chatId: string };\n initDirectStreamingSession: (\n sessionKey: string,\n channel: string,\n chatId: string,\n ) => SessionContext;\n registerWebchatSsePublisher: (\n sessionKey: string,\n publisher: (event: { type: string; [key: string]: unknown }) => void,\n ) => void;\n unregisterWebchatSsePublisher: (sessionKey: string) => void;\n agentManager: AgentManager;\n hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;\n hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;\n agentEventHandler: AgentEventHandler;\n sessionStore: SessionStore;\n prepareLoadedSessionMessages: (sessionKey: string, messages: AgentMessage[]) => AgentMessage[];\n modelManager: ModelManager;\n applyResolvedThinkingLevel: (sessionKey: string, thinking?: string | null) => Promise<void>;\n getConfig: () => Config | undefined;\n sessionConfigStore: SessionConfigStore;\n attachmentRootsForSession: (sessionKey: string) => InternalAttachmentRoots;\n agentOrchestrator: Pick<AgentOrchestrator, 'abort'>;\n commandHandler: Pick<CommandHandler, 'executeCommandAndAggregateReply'>;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: DirectStreamInboundAttachment[],\n ) => Promise<DirectStreamInboundAttachment[] | undefined>;\n buildMessageContent: (\n content: string,\n attachments: DirectStreamInboundAttachment[] | undefined,\n sessionKey: string,\n ) => Promise<Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }>>;\n persistAgentSessionMessages: (sessionKey: string) => Promise<void>;\n maybeEmitWebchatTts: (\n sessionKey: string,\n hadInboundVoice: boolean,\n ) => Promise<{ type: 'tts_audio'; workspaceRelativePath: string; mimeType: string; name: string } | null>;\n endDirectRequestContext: () => void;\n}\n\nexport interface ProcessDirectStreamingInput {\n content: string;\n sessionKey?: string;\n attachments?: DirectStreamInboundAttachment[];\n thinking?: string;\n signal?: AbortSignal;\n}\n\nexport type ProcessDirectStreamingSseEvent = { type: string; [key: string]: unknown };\n\nexport async function* runProcessDirectStreaming(\n deps: ProcessDirectStreamingDeps,\n input: ProcessDirectStreamingInput,\n): AsyncGenerator<ProcessDirectStreamingSseEvent, void, unknown> {\n const {\n log,\n parseSessionKey,\n initDirectStreamingSession,\n registerWebchatSsePublisher,\n unregisterWebchatSsePublisher,\n agentManager,\n hydrateSessionWorkspaceFromStore,\n hydrateSessionModelFromStore,\n agentEventHandler,\n sessionStore,\n prepareLoadedSessionMessages,\n modelManager,\n applyResolvedThinkingLevel,\n getConfig,\n sessionConfigStore,\n attachmentRootsForSession,\n agentOrchestrator,\n commandHandler,\n prepareInboundAttachments,\n buildMessageContent,\n persistAgentSessionMessages,\n maybeEmitWebchatTts,\n endDirectRequestContext,\n } = deps;\n\n const sessionKey = input.sessionKey ?? 'cli:direct';\n const { channel, chatId } = parseSessionKey(sessionKey);\n const context = initDirectStreamingSession(sessionKey, channel, chatId);\n\n const eventQueue: ProcessDirectStreamingSseEvent[] = [];\n let resolveWaiting: (() => void) | null = null;\n let agentDone = false;\n\n let lastSentContent = '';\n let lastSentThinking = '';\n let reasoningLevel: ReasoningLevel = 'off';\n\n const enqueueSseEvent = (event: ProcessDirectStreamingSseEvent) => {\n eventQueue.push(event);\n if (resolveWaiting) {\n resolveWaiting();\n resolveWaiting = null;\n }\n };\n\n const pushEvent = (event: ProcessDirectStreamingSseEvent) => {\n const visible = applyReasoningVisibilityToSseEvent(event, reasoningLevel);\n if (visible !== null) {\n enqueueSseEvent(visible);\n }\n };\n\n if (channel === 'webchat') {\n registerWebchatSsePublisher(sessionKey, (e) => pushEvent(e));\n }\n\n const signal = input.signal;\n let userAborted = false;\n let abortHandled = false;\n\n await hydrateSessionWorkspaceFromStore(sessionKey);\n const agent = agentManager.getOrCreateAgent(sessionKey);\n await hydrateSessionModelFromStore(sessionKey);\n const unsubscribeStreaming = agent.subscribe((event: AgentEvent) => {\n agentEventHandler.handle(event, context);\n\n switch (event.type) {\n case 'tool_execution_start': {\n const toolEvent = event as Extract<AgentEvent, { type: 'tool_execution_start' }>;\n const toolName =\n typeof toolEvent.toolName === 'string' && toolEvent.toolName.trim()\n ? toolEvent.toolName.trim()\n : 'unknown';\n pushEvent({\n type: 'tool_start',\n toolName,\n args: toolEvent.args,\n });\n break;\n }\n case 'tool_execution_end': {\n const toolEvent = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n const toolName =\n typeof toolEvent.toolName === 'string' && toolEvent.toolName.trim()\n ? toolEvent.toolName.trim()\n : 'unknown';\n pushEvent({\n type: 'tool_end',\n toolName,\n isError: toolEvent.isError,\n result: serializeAgentToolResultForSse(toolEvent.result),\n });\n break;\n }\n case 'message_update': {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_update' }>;\n if (msgEvent.message?.role === 'assistant') {\n const msgContent = msgEvent.message.content;\n const blocks = Array.isArray(msgContent)\n ? (msgContent as Array<{ type: string; text?: string }>)\n : undefined;\n const fullText = blocks ? extractTextContent(blocks) : String(msgContent);\n const thinkingFromBlocks = blocks ? extractThinkingContent(blocks) : '';\n const thinkingFromReasoning = extractThinkingFromAssistantMessage(msgEvent.message);\n const thinkingText =\n thinkingFromReasoning.length >= thinkingFromBlocks.length\n ? thinkingFromReasoning\n : thinkingFromBlocks;\n\n if (fullText.length > lastSentContent.length) {\n const delta = fullText.slice(lastSentContent.length);\n if (delta) {\n pushEvent({ type: 'token', content: delta });\n lastSentContent = fullText;\n }\n } else if (fullText.length < lastSentContent.length) {\n pushEvent({ type: 'token', content: fullText });\n lastSentContent = fullText;\n }\n\n if (thinkingText.length > lastSentThinking.length) {\n const thDelta = thinkingText.slice(lastSentThinking.length);\n if (thDelta) {\n pushEvent({ type: 'thinking', content: thDelta, delta: true });\n lastSentThinking = thinkingText;\n }\n } else if (thinkingText.length < lastSentThinking.length) {\n pushEvent({ type: 'thinking', content: thinkingText, delta: false });\n lastSentThinking = thinkingText;\n }\n }\n break;\n }\n case 'message_start': {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_start' }>;\n if (msgEvent.message?.role === 'assistant') {\n lastSentContent = '';\n lastSentThinking = '';\n pushEvent({ type: 'thinking', status: 'started' });\n }\n break;\n }\n case 'message_end': {\n pushEvent({ type: 'message_end' });\n break;\n }\n case 'agent_start': {\n pushEvent({ type: 'progress', stage: 'thinking', message: 'Thinking...' });\n break;\n }\n case 'agent_end': {\n pushEvent({ type: 'progress', stage: 'idle', message: 'Done' });\n break;\n }\n default:\n break;\n }\n });\n\n try {\n const prepared = await prepareInboundAttachments(sessionKey, input.attachments);\n let loaded = await sessionStore.load(sessionKey);\n const lastMsg = loaded[loaded.length - 1] as { role?: string; webchatEarlySave?: boolean } | undefined;\n if (lastMsg?.role === 'user' && lastMsg.webchatEarlySave === true) {\n loaded = loaded.slice(0, -1);\n }\n agent.state.messages = prepareLoadedSessionMessages(sessionKey, loaded);\n\n await modelManager.applyModelForSession(agent, sessionKey);\n await applyResolvedThinkingLevel(sessionKey, input.thinking);\n {\n const defReason = (getConfig()?.agents?.defaults?.reasoningDefault ?? 'off') as ReasoningLevel;\n reasoningLevel = await resolveEffectiveReasoningLevel(sessionConfigStore, sessionKey, defReason);\n }\n\n const sttCfg = mergeSttConfigFromAppConfig(getConfig()?.stt);\n const { text: mergedUserText, inboundVoice } = await mergeVoiceTranscriptsIntoUserText(\n attachmentRootsForSession(sessionKey),\n prepared,\n input.content,\n sttCfg,\n );\n\n const armAbort = () => {\n if (abortHandled) {\n return;\n }\n abortHandled = true;\n userAborted = true;\n agentOrchestrator.abort(sessionKey);\n agentDone = true;\n pushEvent({ type: '__done__' });\n };\n if (signal) {\n if (signal.aborted) {\n armAbort();\n } else {\n signal.addEventListener('abort', armAbort, { once: true });\n }\n }\n\n const commandInfo = parseSlashCommand(mergedUserText);\n let ranSlashCommand = false;\n if (!abortHandled && commandInfo && commandRegistry.has(commandInfo.command)) {\n ranSlashCommand = true;\n const { aggregatedText } = await commandHandler.executeCommandAndAggregateReply(\n commandInfo.command,\n commandInfo.args,\n {\n sessionKey,\n channel,\n chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n },\n );\n if (aggregatedText) {\n pushEvent({ type: 'token', content: aggregatedText });\n }\n pushEvent({ type: '__done__' });\n agentDone = true;\n }\n\n let messageContent:\n | Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }>\n | undefined;\n if (!abortHandled && !ranSlashCommand) {\n const textForAgent = mergedUserText.trimStart().startsWith('/skill:')\n ? agentManager.expandSkillUserText(mergedUserText)\n : mergedUserText;\n messageContent = await buildMessageContent(textForAgent, prepared, sessionKey);\n }\n\n if (!abortHandled && !ranSlashCommand && messageContent !== undefined) {\n const agentPromise = (async () => {\n const userMessage = {\n role: 'user' as const,\n content: messageContent,\n timestamp: Date.now(),\n };\n const userPlain = extractAgentUserPlainText(userMessage);\n const userMessageForModel = await agentManager.applyMemoryPrefetchToUserMessage(\n userMessage,\n sessionKey,\n );\n await runAgentTurnWithModelFallbacks({\n agent,\n sessionKey,\n modelManager,\n userMessage: userMessageForModel,\n log,\n getConfig,\n beforeUserPrompt: () => agentManager.beginBackgroundReviewUserTurn(sessionKey),\n });\n agentManager.afterAgentTurn(sessionKey, userPlain);\n agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n })();\n\n agentPromise\n .then(() => {\n if (abortHandled) {\n return;\n }\n agentDone = true;\n pushEvent({ type: '__done__' });\n })\n .catch((err) => {\n if (abortHandled) {\n return;\n }\n agentDone = true;\n pushEvent({ type: 'error', content: err instanceof Error ? err.message : String(err) });\n pushEvent({ type: '__done__' });\n });\n }\n\n while (true) {\n if (eventQueue.length > 0) {\n const event = eventQueue.shift()!;\n if (event.type === '__done__') break;\n yield event;\n } else if (agentDone) {\n break;\n } else {\n await new Promise<void>((resolve) => {\n resolveWaiting = resolve;\n });\n }\n }\n\n while (eventQueue.length > 0) {\n const event = eventQueue.shift()!;\n if (event.type === '__done__') continue;\n yield event;\n }\n\n await persistAgentSessionMessages(sessionKey);\n\n if (!userAborted) {\n const ttsAudioEvent = await maybeEmitWebchatTts(sessionKey, inboundVoice);\n if (ttsAudioEvent) {\n yield ttsAudioEvent;\n }\n }\n } finally {\n if (channel === 'webchat') {\n unregisterWebchatSsePublisher(sessionKey);\n }\n unsubscribeStreaming();\n endDirectRequestContext();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"process-direct-streaming.js","names":["msgEvent"],"sources":["../../../../src/agent/service/process-direct-streaming.ts"],"sourcesContent":["import type { AgentEvent, AgentMessage } from '@mariozechner/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport type { InternalAttachmentRoots } from '../../channels/attachments/inbound-persist.js';\nimport { commandRegistry } from '../../chat-commands/index.js';\nimport { parseSlashCommand } from '../../chat-commands/command-parse.js';\nimport {\n mergeVoiceTranscriptsIntoUserText,\n mergeSttConfigFromAppConfig,\n} from '../../channels/attachments/voice-stt-webchat.js';\nimport {\n resolveEffectiveReasoningLevel,\n type SessionConfigStore,\n type SessionStore,\n} from '../../session/index.js';\nimport type { SessionContext } from '../session/index.js';\nimport {\n extractTextContent,\n extractThinkingContent,\n extractThinkingFromAssistantMessage,\n} from '../context/workspace.js';\nimport { extractAgentUserPlainText } from '../memory/user-message-text.js';\nimport { runAgentTurnWithModelFallbacks } from '../orchestration/run-agent-turn-with-fallbacks.js';\nimport { applyReasoningVisibilityToSseEvent } from '../streaming/reasoning-visibility-sse.js';\nimport type { ReasoningLevel } from '../transcript/thinking-types.js';\nimport { serializeAgentToolResultForSse } from '../service-inbound-utils.js';\nimport type { AgentManager } from '../agent-manager.js';\nimport type { AgentEventHandler } from '../orchestration/agent-event-handler.js';\nimport type { AgentOrchestrator } from '../orchestration/agent-orchestrator.js';\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { ModelManager } from '../models/index.js';\n\nexport type DirectStreamInboundAttachment = {\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n};\n\n/** Logger shape expected by {@link runAgentTurnWithModelFallbacks}. */\nexport type ProcessDirectStreamLog = {\n info: (obj: Record<string, unknown>, msg: string) => void;\n warn: (obj: Record<string, unknown>, msg: string) => void;\n debug?: (obj: Record<string, unknown>, msg: string) => void;\n};\n\n/**\n * Explicit collaborators for {@link runProcessDirectStreaming} (gateway webchat / CLI direct stream).\n * Bound by `AgentService` from its private fields and methods.\n */\nexport interface ProcessDirectStreamingDeps {\n log: ProcessDirectStreamLog;\n parseSessionKey: (sessionKey: string) => { channel: string; chatId: string };\n initDirectStreamingSession: (\n sessionKey: string,\n channel: string,\n chatId: string,\n ) => SessionContext;\n registerWebchatSsePublisher: (\n sessionKey: string,\n publisher: (event: { type: string; [key: string]: unknown }) => void,\n ) => void;\n unregisterWebchatSsePublisher: (sessionKey: string) => void;\n agentManager: AgentManager;\n hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;\n hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;\n agentEventHandler: AgentEventHandler;\n sessionStore: SessionStore;\n prepareLoadedSessionMessages: (sessionKey: string, messages: AgentMessage[]) => AgentMessage[];\n modelManager: ModelManager;\n applyResolvedThinkingLevel: (sessionKey: string, thinking?: string | null) => Promise<void>;\n getConfig: () => Config | undefined;\n sessionConfigStore: SessionConfigStore;\n attachmentRootsForSession: (sessionKey: string) => InternalAttachmentRoots;\n agentOrchestrator: Pick<AgentOrchestrator, 'abort'>;\n commandHandler: Pick<CommandHandler, 'executeCommandAndAggregateReply'>;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: DirectStreamInboundAttachment[],\n ) => Promise<DirectStreamInboundAttachment[] | undefined>;\n buildMessageContent: (\n content: string,\n attachments: DirectStreamInboundAttachment[] | undefined,\n sessionKey: string,\n ) => Promise<Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }>>;\n persistAgentSessionMessages: (sessionKey: string) => Promise<void>;\n maybeEmitWebchatTts: (\n sessionKey: string,\n hadInboundVoice: boolean,\n ) => Promise<{ type: 'tts_audio'; workspaceRelativePath: string; mimeType: string; name: string } | null>;\n endDirectRequestContext: () => void;\n}\n\nexport interface ProcessDirectStreamingInput {\n content: string;\n sessionKey?: string;\n attachments?: DirectStreamInboundAttachment[];\n thinking?: string;\n signal?: AbortSignal;\n}\n\nexport type ProcessDirectStreamingSseEvent = { type: string; [key: string]: unknown };\n\nexport async function* runProcessDirectStreaming(\n deps: ProcessDirectStreamingDeps,\n input: ProcessDirectStreamingInput,\n): AsyncGenerator<ProcessDirectStreamingSseEvent, void, unknown> {\n const {\n log,\n parseSessionKey,\n initDirectStreamingSession,\n registerWebchatSsePublisher,\n unregisterWebchatSsePublisher,\n agentManager,\n hydrateSessionWorkspaceFromStore,\n hydrateSessionModelFromStore,\n agentEventHandler,\n sessionStore,\n prepareLoadedSessionMessages,\n modelManager,\n applyResolvedThinkingLevel,\n getConfig,\n sessionConfigStore,\n attachmentRootsForSession,\n agentOrchestrator,\n commandHandler,\n prepareInboundAttachments,\n buildMessageContent,\n persistAgentSessionMessages,\n maybeEmitWebchatTts,\n endDirectRequestContext,\n } = deps;\n\n const sessionKey = input.sessionKey ?? 'cli:direct';\n const { channel, chatId } = parseSessionKey(sessionKey);\n const context = initDirectStreamingSession(sessionKey, channel, chatId);\n\n const eventQueue: ProcessDirectStreamingSseEvent[] = [];\n let resolveWaiting: (() => void) | null = null;\n let agentDone = false;\n\n let lastSentContent = '';\n let lastSentThinking = '';\n let reasoningLevel: ReasoningLevel = 'stream';\n\n const enqueueSseEvent = (event: ProcessDirectStreamingSseEvent) => {\n eventQueue.push(event);\n if (resolveWaiting) {\n resolveWaiting();\n resolveWaiting = null;\n }\n };\n\n const pushEvent = (event: ProcessDirectStreamingSseEvent) => {\n const visible = applyReasoningVisibilityToSseEvent(event, reasoningLevel);\n if (visible !== null) {\n enqueueSseEvent(visible);\n }\n };\n\n if (channel === 'webchat') {\n registerWebchatSsePublisher(sessionKey, (e) => pushEvent(e));\n }\n\n const signal = input.signal;\n let userAborted = false;\n let abortHandled = false;\n\n await hydrateSessionWorkspaceFromStore(sessionKey);\n const agent = agentManager.getOrCreateAgent(sessionKey);\n await hydrateSessionModelFromStore(sessionKey);\n const unsubscribeStreaming = agent.subscribe((event: AgentEvent) => {\n agentEventHandler.handle(event, context);\n\n switch (event.type) {\n case 'tool_execution_start': {\n const toolEvent = event as Extract<AgentEvent, { type: 'tool_execution_start' }>;\n const toolName =\n typeof toolEvent.toolName === 'string' && toolEvent.toolName.trim()\n ? toolEvent.toolName.trim()\n : 'unknown';\n pushEvent({\n type: 'tool_start',\n toolCallId: toolEvent.toolCallId,\n toolName,\n args: toolEvent.args,\n });\n break;\n }\n case 'tool_execution_end': {\n const toolEvent = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n const toolName =\n typeof toolEvent.toolName === 'string' && toolEvent.toolName.trim()\n ? toolEvent.toolName.trim()\n : 'unknown';\n pushEvent({\n type: 'tool_end',\n toolCallId: toolEvent.toolCallId,\n toolName,\n isError: toolEvent.isError,\n result: serializeAgentToolResultForSse(toolEvent.result),\n });\n break;\n }\n case 'message_update': {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_update' }>;\n if (msgEvent.message?.role === 'assistant') {\n const msgContent = msgEvent.message.content;\n const blocks = Array.isArray(msgContent)\n ? (msgContent as Array<{ type: string; text?: string }>)\n : undefined;\n const fullText = blocks ? extractTextContent(blocks) : String(msgContent);\n const thinkingFromBlocks = blocks ? extractThinkingContent(blocks) : '';\n const thinkingFromReasoning = extractThinkingFromAssistantMessage(msgEvent.message);\n const thinkingText =\n thinkingFromReasoning.length >= thinkingFromBlocks.length\n ? thinkingFromReasoning\n : thinkingFromBlocks;\n\n if (fullText.length > lastSentContent.length) {\n const delta = fullText.slice(lastSentContent.length);\n if (delta) {\n pushEvent({ type: 'token', content: delta });\n lastSentContent = fullText;\n }\n } else if (fullText.length < lastSentContent.length) {\n pushEvent({ type: 'token', content: fullText });\n lastSentContent = fullText;\n }\n\n if (thinkingText.length > lastSentThinking.length) {\n const thDelta = thinkingText.slice(lastSentThinking.length);\n if (thDelta) {\n pushEvent({ type: 'thinking', content: thDelta, delta: true });\n lastSentThinking = thinkingText;\n }\n } else if (thinkingText.length < lastSentThinking.length) {\n pushEvent({ type: 'thinking', content: thinkingText, delta: false });\n lastSentThinking = thinkingText;\n }\n }\n break;\n }\n case 'message_start': {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_start' }>;\n if (msgEvent.message?.role === 'assistant') {\n lastSentContent = '';\n lastSentThinking = '';\n pushEvent({ type: 'thinking', status: 'started' });\n }\n break;\n }\n case 'message_end': {\n pushEvent({ type: 'message_end' });\n break;\n }\n case 'agent_start': {\n pushEvent({ type: 'progress', stage: 'thinking', message: 'Thinking...' });\n break;\n }\n case 'agent_end': {\n pushEvent({ type: 'progress', stage: 'idle', message: 'Done' });\n break;\n }\n default:\n break;\n }\n });\n\n try {\n const prepared = await prepareInboundAttachments(sessionKey, input.attachments);\n let loaded = await sessionStore.load(sessionKey);\n const lastMsg = loaded[loaded.length - 1] as { role?: string; webchatEarlySave?: boolean } | undefined;\n if (lastMsg?.role === 'user' && lastMsg.webchatEarlySave === true) {\n loaded = loaded.slice(0, -1);\n }\n agent.state.messages = prepareLoadedSessionMessages(sessionKey, loaded);\n\n await modelManager.applyModelForSession(agent, sessionKey);\n await applyResolvedThinkingLevel(sessionKey, input.thinking);\n {\n const defReason = (getConfig()?.agents?.defaults?.reasoningDefault ?? 'stream') as ReasoningLevel;\n reasoningLevel = await resolveEffectiveReasoningLevel(sessionConfigStore, sessionKey, defReason);\n }\n\n const sttCfg = mergeSttConfigFromAppConfig(getConfig()?.stt);\n const { text: mergedUserText, inboundVoice } = await mergeVoiceTranscriptsIntoUserText(\n attachmentRootsForSession(sessionKey),\n prepared,\n input.content,\n sttCfg,\n );\n\n const armAbort = () => {\n if (abortHandled) {\n return;\n }\n abortHandled = true;\n userAborted = true;\n agentOrchestrator.abort(sessionKey);\n agentDone = true;\n pushEvent({ type: '__done__' });\n };\n if (signal) {\n if (signal.aborted) {\n armAbort();\n } else {\n signal.addEventListener('abort', armAbort, { once: true });\n }\n }\n\n const commandInfo = parseSlashCommand(mergedUserText);\n let ranSlashCommand = false;\n /** Plain text to persist for webchat slash turns (SSE can be dropped by the UI; disk is source of truth). */\n let webchatSlashReceipt: string | undefined;\n if (!abortHandled && commandInfo) {\n if (commandRegistry.has(commandInfo.command)) {\n ranSlashCommand = true;\n try {\n const { aggregatedText } = await commandHandler.executeCommandAndAggregateReply(\n commandInfo.command,\n commandInfo.args,\n {\n sessionKey,\n channel,\n chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n },\n );\n if (aggregatedText?.trim()) {\n webchatSlashReceipt = aggregatedText.trim();\n pushEvent({ type: 'token', content: webchatSlashReceipt });\n } else if (channel === 'webchat') {\n webchatSlashReceipt =\n 'Command finished with no assistant text. For `/goal`, that usually means the goal was saved; the assistant will not run until your next normal message (or an auto-continuation after an assistant reply).';\n pushEvent({ type: 'token', content: webchatSlashReceipt });\n }\n } catch (cmdErr) {\n const em = cmdErr instanceof Error ? cmdErr.message : String(cmdErr);\n log.warn({ err: cmdErr, sessionKey, command: commandInfo.command }, `Slash command failed: ${em}`);\n webchatSlashReceipt = `Command error: ${em}`;\n pushEvent({ type: 'token', content: webchatSlashReceipt });\n }\n pushEvent({ type: '__done__' });\n agentDone = true;\n } else if (commandInfo.command === 'goal') {\n // `/goal` is provided by the `standing-goal` bundled extension; avoid silent LLM fallback.\n ranSlashCommand = true;\n webchatSlashReceipt =\n 'The /goal command is not available: the `standing-goal` extension is not loaded.\\n\\n' +\n 'Fix: add `\"standing-goal\"` to `extensions.enabled` in your xopc config (or remove it from `extensions.disabled`), then restart the gateway. ' +\n 'If you use a strict allowlist, include `standing-goal` in `extensions.enabled`.';\n pushEvent({\n type: 'token',\n content: webchatSlashReceipt,\n });\n pushEvent({ type: '__done__' });\n agentDone = true;\n }\n }\n\n let messageContent:\n | Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }>\n | undefined;\n if (!abortHandled && !ranSlashCommand) {\n const textForAgent = mergedUserText.trimStart().startsWith('/skill:')\n ? agentManager.expandSkillUserText(mergedUserText)\n : mergedUserText;\n messageContent = await buildMessageContent(textForAgent, prepared, sessionKey);\n }\n\n if (!abortHandled && !ranSlashCommand && messageContent !== undefined) {\n const agentPromise = (async () => {\n const userMessage = {\n role: 'user' as const,\n content: messageContent,\n timestamp: Date.now(),\n };\n const userPlain = extractAgentUserPlainText(userMessage);\n const userMessageForModel = await agentManager.applyMemoryPrefetchToUserMessage(\n userMessage,\n sessionKey,\n );\n await runAgentTurnWithModelFallbacks({\n agent,\n sessionKey,\n modelManager,\n userMessage: userMessageForModel,\n log,\n getConfig,\n beforeUserPrompt: () => agentManager.beginBackgroundReviewUserTurn(sessionKey),\n });\n agentManager.afterAgentTurn(sessionKey, userPlain);\n agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n })();\n\n agentPromise\n .then(() => {\n if (abortHandled) {\n return;\n }\n agentDone = true;\n pushEvent({ type: '__done__' });\n })\n .catch((err) => {\n if (abortHandled) {\n return;\n }\n agentDone = true;\n pushEvent({ type: 'error', content: err instanceof Error ? err.message : String(err) });\n pushEvent({ type: '__done__' });\n });\n }\n\n while (true) {\n if (eventQueue.length > 0) {\n const event = eventQueue.shift()!;\n if (event.type === '__done__') break;\n yield event;\n } else if (agentDone) {\n break;\n } else {\n await new Promise<void>((resolve) => {\n resolveWaiting = resolve;\n });\n }\n }\n\n while (eventQueue.length > 0) {\n const event = eventQueue.shift()!;\n if (event.type === '__done__') continue;\n yield event;\n }\n\n // Persist slash command text to the session file so the gateway console can show a receipt even when\n // the browser drops SSE tokens (e.g. session key mismatch during hydration).\n if (channel === 'webchat' && ranSlashCommand && webchatSlashReceipt?.trim()) {\n try {\n const loaded = await sessionStore.load(sessionKey);\n const text = webchatSlashReceipt.trim();\n // Minimal assistant row for session JSON; full AssistantMessage metadata is LLM-turn specific.\n const assistantMsg = {\n role: 'assistant' as const,\n content: [{ type: 'text' as const, text }],\n timestamp: Date.now(),\n } as AgentMessage;\n await sessionStore.save(sessionKey, [...loaded, assistantMsg]);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Failed to persist webchat slash command receipt');\n }\n }\n\n // Slash-only turns never hydrate the early-saved user row into pi-agent state; persisting here would\n // write an empty/stale transcript over the gateway `SessionManager` copy (and break e.g. `/goal`).\n if (!ranSlashCommand) {\n await persistAgentSessionMessages(sessionKey);\n }\n\n if (!userAborted) {\n const ttsAudioEvent = await maybeEmitWebchatTts(sessionKey, inboundVoice);\n if (ttsAudioEvent) {\n yield ttsAudioEvent;\n }\n }\n } finally {\n if (channel === 'webchat') {\n unregisterWebchatSsePublisher(sessionKey);\n }\n unsubscribeStreaming();\n endDirectRequestContext();\n }\n}\n"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import type { AgentMessage, ThinkingLevel } from '@mariozechner/pi-agent-core';
|
|
|
2
2
|
import { type MessageBus } from '../infra/bus/index.js';
|
|
3
3
|
import { type Config } from '../config/schema.js';
|
|
4
4
|
import type { ChannelManager } from '../channels/manager.js';
|
|
5
|
+
import type { SessionDetail } from '../session/types.js';
|
|
5
6
|
import { type ReasoningLevel } from './transcript/thinking-types.js';
|
|
6
7
|
import { type SkillCatalogEntry } from './agent-manager.js';
|
|
7
8
|
import type { AgentServiceConfig, StreamHandle } from './service.types.js';
|
|
@@ -74,6 +75,18 @@ export declare class AgentService {
|
|
|
74
75
|
private hydrateSessionModelFromStore;
|
|
75
76
|
setStreamHandle(handle: StreamHandle): void;
|
|
76
77
|
clearStreamHandle(): void;
|
|
78
|
+
/** Last assistant visible plain text for a session (e.g. after a webchat stream). */
|
|
79
|
+
getLastAssistantPlainText(sessionKey: string): string;
|
|
80
|
+
/**
|
|
81
|
+
* Notify extensions after a webchat direct stream ends (Gateway calls after session run lock is released).
|
|
82
|
+
*/
|
|
83
|
+
emitWebchatTurnComplete(payload: {
|
|
84
|
+
sessionKey: string;
|
|
85
|
+
inboundUserText: string;
|
|
86
|
+
assistantPlainText: string;
|
|
87
|
+
aborted: boolean;
|
|
88
|
+
streamError?: string;
|
|
89
|
+
}): Promise<void>;
|
|
77
90
|
start(): Promise<void>;
|
|
78
91
|
stop(): Promise<void>;
|
|
79
92
|
/**
|
|
@@ -116,6 +129,8 @@ export declare class AgentService {
|
|
|
116
129
|
workspaceRelativePath?: string;
|
|
117
130
|
}> | undefined>;
|
|
118
131
|
private endDirectRequestContext;
|
|
132
|
+
/** Full session snapshot (metadata + API-shaped messages), e.g. embedded TUI history. */
|
|
133
|
+
loadSessionDetail(sessionKey: string): Promise<SessionDetail | null>;
|
|
119
134
|
compactSession(sessionKey: string, options?: {
|
|
120
135
|
instructions?: string;
|
|
121
136
|
force?: boolean;
|
|
@@ -400,6 +400,22 @@ var AgentService = class {
|
|
|
400
400
|
this.streamManager.clearHandle();
|
|
401
401
|
this.feedbackCoordinator.endTask();
|
|
402
402
|
}
|
|
403
|
+
/** Last assistant visible plain text for a session (e.g. after a webchat stream). */
|
|
404
|
+
getLastAssistantPlainText(sessionKey) {
|
|
405
|
+
return this.agentManager.getLastAssistantContent(sessionKey) ?? "";
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Notify extensions after a webchat direct stream ends (Gateway calls after session run lock is released).
|
|
409
|
+
*/
|
|
410
|
+
async emitWebchatTurnComplete(payload) {
|
|
411
|
+
await this.hookHandler.triggerWithSessionKey(payload.sessionKey, "webchat_turn_complete", {
|
|
412
|
+
sessionKey: payload.sessionKey,
|
|
413
|
+
inboundUserText: payload.inboundUserText,
|
|
414
|
+
assistantPlainText: payload.assistantPlainText,
|
|
415
|
+
aborted: payload.aborted,
|
|
416
|
+
...payload.streamError !== void 0 ? { streamError: payload.streamError } : {}
|
|
417
|
+
});
|
|
418
|
+
}
|
|
403
419
|
async start() {
|
|
404
420
|
this.running = true;
|
|
405
421
|
await this.sessionConfigStore.initialize();
|
|
@@ -685,6 +701,10 @@ var AgentService = class {
|
|
|
685
701
|
this.feedbackCoordinator.clearContext();
|
|
686
702
|
this.contextMiddleware.onResponse();
|
|
687
703
|
}
|
|
704
|
+
/** Full session snapshot (metadata + API-shaped messages), e.g. embedded TUI history. */
|
|
705
|
+
async loadSessionDetail(sessionKey) {
|
|
706
|
+
return this.sessionStore.get(sessionKey);
|
|
707
|
+
}
|
|
688
708
|
async compactSession(sessionKey, options) {
|
|
689
709
|
const messages = await this.sessionStore.load(sessionKey);
|
|
690
710
|
const contextWindow = this.getContextWindow();
|
|
@@ -856,7 +876,7 @@ var AgentService = class {
|
|
|
856
876
|
if (profileModelRef) this.modelManager.setSessionProfileDefault(sessionKey, profileModelRef);
|
|
857
877
|
const defThink = cfg.agents?.defaults?.thinkingDefault ?? "medium";
|
|
858
878
|
const level = await resolveEffectiveThinkingLevel(this.sessionConfigStore, sessionKey, null, defThink);
|
|
859
|
-
const defReason = cfg.agents?.defaults?.reasoningDefault ?? "
|
|
879
|
+
const defReason = cfg.agents?.defaults?.reasoningDefault ?? "stream";
|
|
860
880
|
const reasoningLevel = await resolveEffectiveReasoningLevel(this.sessionConfigStore, sessionKey, defReason);
|
|
861
881
|
return {
|
|
862
882
|
thinkingLevel: level,
|