@opengsd/gsd-pi 1.0.2-dev.50223bc → 1.0.2-dev.5961fbf
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/resource-loader.d.ts +5 -0
- package/dist/resource-loader.js +24 -8
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/loop.js +19 -0
- package/dist/resources/extensions/gsd/auto/phases.js +1 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +2 -54
- package/dist/resources/extensions/gsd/worktree-post-create-hook.js +117 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
- package/dist/web/standalone/.next/server/chunks/1834.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/dist/web/standalone/package.json +0 -1
- package/dist/worktree-cli.d.ts +0 -2
- package/dist/worktree-cli.js +21 -9
- package/package.json +9 -4
- package/packages/cloud-mcp-gateway/bin/gsd-cloud-mcp-gateway.js +14 -0
- package/packages/cloud-mcp-gateway/package.json +5 -4
- package/packages/contracts/package.json +2 -2
- package/packages/daemon/bin/gsd-daemon.js +14 -0
- package/packages/daemon/bin/gsd-mcp-runtime.js +14 -0
- package/packages/daemon/bin/gsd-mcp.js +14 -0
- package/packages/daemon/dist/channel-manager.d.ts +53 -0
- package/packages/daemon/dist/channel-manager.d.ts.map +1 -0
- package/packages/daemon/dist/channel-manager.js +167 -0
- package/packages/daemon/dist/channel-manager.js.map +1 -0
- package/packages/daemon/dist/cli.d.ts +3 -0
- package/packages/daemon/dist/cli.d.ts.map +1 -0
- package/packages/daemon/dist/cli.js +94 -0
- package/packages/daemon/dist/cli.js.map +1 -0
- package/packages/daemon/dist/cloud-cli.d.ts +7 -0
- package/packages/daemon/dist/cloud-cli.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-cli.js +96 -0
- package/packages/daemon/dist/cloud-cli.js.map +1 -0
- package/packages/daemon/dist/cloud-config.d.ts +18 -0
- package/packages/daemon/dist/cloud-config.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-config.js +209 -0
- package/packages/daemon/dist/cloud-config.js.map +1 -0
- package/packages/daemon/dist/cloud-config.test.d.ts +2 -0
- package/packages/daemon/dist/cloud-config.test.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-config.test.js +132 -0
- package/packages/daemon/dist/cloud-config.test.js.map +1 -0
- package/packages/daemon/dist/cloud-runtime.d.ts +26 -0
- package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-runtime.js +180 -0
- package/packages/daemon/dist/cloud-runtime.js.map +1 -0
- package/packages/daemon/dist/cloud-runtime.test.d.ts +2 -0
- package/packages/daemon/dist/cloud-runtime.test.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-runtime.test.js +28 -0
- package/packages/daemon/dist/cloud-runtime.test.js.map +1 -0
- package/packages/daemon/dist/cloud-token.d.ts +3 -0
- package/packages/daemon/dist/cloud-token.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-token.js +37 -0
- package/packages/daemon/dist/cloud-token.js.map +1 -0
- package/packages/daemon/dist/commands.d.ts +25 -0
- package/packages/daemon/dist/commands.d.ts.map +1 -0
- package/packages/daemon/dist/commands.js +81 -0
- package/packages/daemon/dist/commands.js.map +1 -0
- package/packages/daemon/dist/config.d.ts +17 -0
- package/packages/daemon/dist/config.d.ts.map +1 -0
- package/packages/daemon/dist/config.js +146 -0
- package/packages/daemon/dist/config.js.map +1 -0
- package/packages/daemon/dist/daemon.d.ts +38 -0
- package/packages/daemon/dist/daemon.d.ts.map +1 -0
- package/packages/daemon/dist/daemon.js +194 -0
- package/packages/daemon/dist/daemon.js.map +1 -0
- package/packages/daemon/dist/daemon.test.d.ts +2 -0
- package/packages/daemon/dist/daemon.test.d.ts.map +1 -0
- package/packages/daemon/dist/daemon.test.js +692 -0
- package/packages/daemon/dist/daemon.test.js.map +1 -0
- package/packages/daemon/dist/discord-bot.d.ts +70 -0
- package/packages/daemon/dist/discord-bot.d.ts.map +1 -0
- package/packages/daemon/dist/discord-bot.js +433 -0
- package/packages/daemon/dist/discord-bot.js.map +1 -0
- package/packages/daemon/dist/discord-bot.test.d.ts +2 -0
- package/packages/daemon/dist/discord-bot.test.d.ts.map +1 -0
- package/packages/daemon/dist/discord-bot.test.js +667 -0
- package/packages/daemon/dist/discord-bot.test.js.map +1 -0
- package/packages/daemon/dist/event-bridge.d.ts +72 -0
- package/packages/daemon/dist/event-bridge.d.ts.map +1 -0
- package/packages/daemon/dist/event-bridge.js +366 -0
- package/packages/daemon/dist/event-bridge.js.map +1 -0
- package/packages/daemon/dist/event-bridge.test.d.ts +9 -0
- package/packages/daemon/dist/event-bridge.test.d.ts.map +1 -0
- package/packages/daemon/dist/event-bridge.test.js +528 -0
- package/packages/daemon/dist/event-bridge.test.js.map +1 -0
- package/packages/daemon/dist/event-formatter.d.ts +34 -0
- package/packages/daemon/dist/event-formatter.d.ts.map +1 -0
- package/packages/daemon/dist/event-formatter.js +355 -0
- package/packages/daemon/dist/event-formatter.js.map +1 -0
- package/packages/daemon/dist/event-formatter.test.d.ts +2 -0
- package/packages/daemon/dist/event-formatter.test.d.ts.map +1 -0
- package/packages/daemon/dist/event-formatter.test.js +333 -0
- package/packages/daemon/dist/event-formatter.test.js.map +1 -0
- package/packages/daemon/dist/index.d.ts +25 -0
- package/packages/daemon/dist/index.d.ts.map +1 -0
- package/packages/daemon/dist/index.js +17 -0
- package/packages/daemon/dist/index.js.map +1 -0
- package/packages/daemon/dist/launchd.d.ts +49 -0
- package/packages/daemon/dist/launchd.d.ts.map +1 -0
- package/packages/daemon/dist/launchd.js +188 -0
- package/packages/daemon/dist/launchd.js.map +1 -0
- package/packages/daemon/dist/launchd.test.d.ts +2 -0
- package/packages/daemon/dist/launchd.test.d.ts.map +1 -0
- package/packages/daemon/dist/launchd.test.js +296 -0
- package/packages/daemon/dist/launchd.test.js.map +1 -0
- package/packages/daemon/dist/local-tool-executor.d.ts +22 -0
- package/packages/daemon/dist/local-tool-executor.d.ts.map +1 -0
- package/packages/daemon/dist/local-tool-executor.js +307 -0
- package/packages/daemon/dist/local-tool-executor.js.map +1 -0
- package/packages/daemon/dist/local-tool-executor.test.d.ts +2 -0
- package/packages/daemon/dist/local-tool-executor.test.d.ts.map +1 -0
- package/packages/daemon/dist/local-tool-executor.test.js +111 -0
- package/packages/daemon/dist/local-tool-executor.test.js.map +1 -0
- package/packages/daemon/dist/logger.d.ts +25 -0
- package/packages/daemon/dist/logger.d.ts.map +1 -0
- package/packages/daemon/dist/logger.js +72 -0
- package/packages/daemon/dist/logger.js.map +1 -0
- package/packages/daemon/dist/mcp-cli.d.ts +3 -0
- package/packages/daemon/dist/mcp-cli.d.ts.map +1 -0
- package/packages/daemon/dist/mcp-cli.js +8 -0
- package/packages/daemon/dist/mcp-cli.js.map +1 -0
- package/packages/daemon/dist/mcp-cli.test.d.ts +2 -0
- package/packages/daemon/dist/mcp-cli.test.d.ts.map +1 -0
- package/packages/daemon/dist/mcp-cli.test.js +13 -0
- package/packages/daemon/dist/mcp-cli.test.js.map +1 -0
- package/packages/daemon/dist/mcp-runtime-cli.d.ts +3 -0
- package/packages/daemon/dist/mcp-runtime-cli.d.ts.map +1 -0
- package/packages/daemon/dist/mcp-runtime-cli.js +8 -0
- package/packages/daemon/dist/mcp-runtime-cli.js.map +1 -0
- package/packages/daemon/dist/message-batcher.d.ts +78 -0
- package/packages/daemon/dist/message-batcher.d.ts.map +1 -0
- package/packages/daemon/dist/message-batcher.js +173 -0
- package/packages/daemon/dist/message-batcher.js.map +1 -0
- package/packages/daemon/dist/message-batcher.test.d.ts +2 -0
- package/packages/daemon/dist/message-batcher.test.d.ts.map +1 -0
- package/packages/daemon/dist/message-batcher.test.js +242 -0
- package/packages/daemon/dist/message-batcher.test.js.map +1 -0
- package/packages/daemon/dist/orchestrator.d.ts +98 -0
- package/packages/daemon/dist/orchestrator.d.ts.map +1 -0
- package/packages/daemon/dist/orchestrator.js +359 -0
- package/packages/daemon/dist/orchestrator.js.map +1 -0
- package/packages/daemon/dist/orchestrator.test.d.ts +8 -0
- package/packages/daemon/dist/orchestrator.test.d.ts.map +1 -0
- package/packages/daemon/dist/orchestrator.test.js +425 -0
- package/packages/daemon/dist/orchestrator.test.js.map +1 -0
- package/packages/daemon/dist/project-scanner.d.ts +18 -0
- package/packages/daemon/dist/project-scanner.d.ts.map +1 -0
- package/packages/daemon/dist/project-scanner.js +90 -0
- package/packages/daemon/dist/project-scanner.js.map +1 -0
- package/packages/daemon/dist/project-scanner.test.d.ts +5 -0
- package/packages/daemon/dist/project-scanner.test.d.ts.map +1 -0
- package/packages/daemon/dist/project-scanner.test.js +183 -0
- package/packages/daemon/dist/project-scanner.test.js.map +1 -0
- package/packages/daemon/dist/session-manager.d.ts +70 -0
- package/packages/daemon/dist/session-manager.d.ts.map +1 -0
- package/packages/daemon/dist/session-manager.js +358 -0
- package/packages/daemon/dist/session-manager.js.map +1 -0
- package/packages/daemon/dist/session-manager.test.d.ts +9 -0
- package/packages/daemon/dist/session-manager.test.d.ts.map +1 -0
- package/packages/daemon/dist/session-manager.test.js +616 -0
- package/packages/daemon/dist/session-manager.test.js.map +1 -0
- package/packages/daemon/dist/types.d.ts +133 -0
- package/packages/daemon/dist/types.d.ts.map +1 -0
- package/packages/daemon/dist/types.js +8 -0
- package/packages/daemon/dist/types.js.map +1 -0
- package/packages/daemon/dist/verbosity.d.ts +27 -0
- package/packages/daemon/dist/verbosity.d.ts.map +1 -0
- package/packages/daemon/dist/verbosity.js +86 -0
- package/packages/daemon/dist/verbosity.js.map +1 -0
- package/packages/daemon/dist/verbosity.test.d.ts +2 -0
- package/packages/daemon/dist/verbosity.test.d.ts.map +1 -0
- package/packages/daemon/dist/verbosity.test.js +136 -0
- package/packages/daemon/dist/verbosity.test.js.map +1 -0
- package/packages/daemon/package.json +9 -8
- package/packages/gsd-agent-core/package.json +6 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +2 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +8 -8
- package/packages/mcp-server/bin/gsd-mcp-server.js +14 -0
- package/packages/mcp-server/package.json +6 -5
- package/packages/native/package.json +3 -3
- package/packages/pi-agent-core/package.json +4 -4
- package/packages/pi-ai/bin/pi-ai.js +14 -0
- package/packages/pi-ai/dist/models.generated.d.ts +0 -17
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +18 -35
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +5 -4
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/read.js +5 -3
- package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
- package/packages/pi-coding-agent/package.json +9 -9
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +3 -3
- package/pkg/package.json +1 -1
- package/scripts/ensure-workspace-builds.cjs +4 -4
- package/scripts/install/deps.js +10 -0
- package/src/resources/extensions/gsd/auto/loop.ts +22 -0
- package/src/resources/extensions/gsd/auto/phases.ts +1 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +2 -56
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +141 -1
- package/src/resources/extensions/gsd/worktree-post-create-hook.ts +127 -0
- package/dist/tsconfig.extensions.tsbuildinfo +0 -1
- /package/dist/web/standalone/.next/static/{JP7xjsa5zSaO76XhE-mFJ → spUYLkQXoHJyxYOMH9VQy}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{JP7xjsa5zSaO76XhE-mFJ → spUYLkQXoHJyxYOMH9VQy}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* event-formatter.ts — Pure functions mapping RPC event types to Discord embeds.
|
|
3
|
+
*
|
|
4
|
+
* Each formatter returns a FormattedEvent (content string + optional EmbedBuilder +
|
|
5
|
+
* optional ActionRow components). Distinct embed colors per category:
|
|
6
|
+
* green = success / completion
|
|
7
|
+
* red = error
|
|
8
|
+
* yellow = blocker (needs attention)
|
|
9
|
+
* blue = info / session lifecycle
|
|
10
|
+
* grey = tool / generic
|
|
11
|
+
*/
|
|
12
|
+
import type { SdkAgentEvent } from '@opengsd/contracts';
|
|
13
|
+
import type { FormattedEvent, PendingBlocker } from './types.js';
|
|
14
|
+
export declare function formatToolStart(event: SdkAgentEvent): FormattedEvent;
|
|
15
|
+
export declare function formatToolEnd(event: SdkAgentEvent): FormattedEvent;
|
|
16
|
+
export declare function formatMessage(event: SdkAgentEvent): FormattedEvent;
|
|
17
|
+
/**
|
|
18
|
+
* Format a blocker (extension_ui_request needing user response).
|
|
19
|
+
* Produces an embed with @mention and interactive buttons for select/confirm,
|
|
20
|
+
* or text instructions for input/editor.
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatBlocker(blocker: PendingBlocker, ownerId: string): FormattedEvent;
|
|
23
|
+
export declare function formatCompletion(event: SdkAgentEvent): FormattedEvent;
|
|
24
|
+
export declare function formatError(sessionId: string, error: string): FormattedEvent;
|
|
25
|
+
export declare function formatCostUpdate(event: SdkAgentEvent): FormattedEvent;
|
|
26
|
+
export declare function formatSessionStarted(projectName: string): FormattedEvent;
|
|
27
|
+
export declare function formatTaskTransition(event: SdkAgentEvent): FormattedEvent;
|
|
28
|
+
export declare function formatGenericEvent(event: SdkAgentEvent): FormattedEvent;
|
|
29
|
+
/**
|
|
30
|
+
* Format any SdkAgentEvent for Discord. Falls back to formatGenericEvent
|
|
31
|
+
* for unknown types.
|
|
32
|
+
*/
|
|
33
|
+
export declare function formatEvent(event: SdkAgentEvent, ownerId?: string): FormattedEvent;
|
|
34
|
+
//# sourceMappingURL=event-formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-formatter.d.ts","sourceRoot":"","sources":["../src/event-formatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAyB,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA+CjE,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAapE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAsBlE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAwDlE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,GACd,cAAc,CA6FhB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CA0BrE;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,CAS5E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAsBrE;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAQxE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAkBzE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAevE;AAMD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,cAAc,CAqBlF"}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* event-formatter.ts — Pure functions mapping RPC event types to Discord embeds.
|
|
3
|
+
*
|
|
4
|
+
* Each formatter returns a FormattedEvent (content string + optional EmbedBuilder +
|
|
5
|
+
* optional ActionRow components). Distinct embed colors per category:
|
|
6
|
+
* green = success / completion
|
|
7
|
+
* red = error
|
|
8
|
+
* yellow = blocker (needs attention)
|
|
9
|
+
* blue = info / session lifecycle
|
|
10
|
+
* grey = tool / generic
|
|
11
|
+
*/
|
|
12
|
+
import { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Color palette
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
const COLOR = {
|
|
17
|
+
success: 0x2ecc71, // green
|
|
18
|
+
error: 0xe74c3c, // red
|
|
19
|
+
blocker: 0xf1c40f, // yellow
|
|
20
|
+
info: 0x3498db, // blue
|
|
21
|
+
tool: 0x95a5a6, // grey
|
|
22
|
+
};
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Helpers
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/** Truncate a string to maxLen, appending ellipsis if truncated. */
|
|
27
|
+
function truncate(s, maxLen) {
|
|
28
|
+
if (s.length <= maxLen)
|
|
29
|
+
return s;
|
|
30
|
+
return s.slice(0, maxLen - 1) + '…';
|
|
31
|
+
}
|
|
32
|
+
/** Safe string extraction from an unknown field. */
|
|
33
|
+
function str(value, fallback = '') {
|
|
34
|
+
if (typeof value === 'string')
|
|
35
|
+
return value;
|
|
36
|
+
if (value == null)
|
|
37
|
+
return fallback;
|
|
38
|
+
return String(value);
|
|
39
|
+
}
|
|
40
|
+
/** Safe number extraction. */
|
|
41
|
+
function num(value, fallback = 0) {
|
|
42
|
+
if (typeof value === 'number' && !Number.isNaN(value))
|
|
43
|
+
return value;
|
|
44
|
+
return fallback;
|
|
45
|
+
}
|
|
46
|
+
/** Format a cost value to a readable string. */
|
|
47
|
+
function formatCost(cost) {
|
|
48
|
+
if (cost < 0.01)
|
|
49
|
+
return `$${cost.toFixed(4)}`;
|
|
50
|
+
return `$${cost.toFixed(2)}`;
|
|
51
|
+
}
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Formatters
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
export function formatToolStart(event) {
|
|
56
|
+
const toolName = str(event.name || event.toolName, 'unknown');
|
|
57
|
+
const embed = new EmbedBuilder()
|
|
58
|
+
.setColor(COLOR.tool)
|
|
59
|
+
.setTitle(`🔧 ${truncate(toolName, 60)}`)
|
|
60
|
+
.setTimestamp();
|
|
61
|
+
const input = str(event.input || event.args);
|
|
62
|
+
if (input) {
|
|
63
|
+
embed.setDescription(`\`\`\`\n${truncate(input, 300)}\n\`\`\``);
|
|
64
|
+
}
|
|
65
|
+
return { content: `🔧 Tool: ${toolName}`, embed };
|
|
66
|
+
}
|
|
67
|
+
export function formatToolEnd(event) {
|
|
68
|
+
const toolName = str(event.name || event.toolName, 'unknown');
|
|
69
|
+
const isError = event.isError === true || event.error != null;
|
|
70
|
+
const color = isError ? COLOR.error : COLOR.tool;
|
|
71
|
+
const icon = isError ? '❌' : '✅';
|
|
72
|
+
const embed = new EmbedBuilder()
|
|
73
|
+
.setColor(color)
|
|
74
|
+
.setTitle(`${icon} ${truncate(toolName, 60)}`)
|
|
75
|
+
.setTimestamp();
|
|
76
|
+
const output = str(event.output || event.result);
|
|
77
|
+
if (output) {
|
|
78
|
+
embed.setDescription(`\`\`\`\n${truncate(output, 300)}\n\`\`\``);
|
|
79
|
+
}
|
|
80
|
+
const duration = num(event.duration || event.durationMs);
|
|
81
|
+
if (duration > 0) {
|
|
82
|
+
embed.setFooter({ text: `${(duration / 1000).toFixed(1)}s` });
|
|
83
|
+
}
|
|
84
|
+
return { content: `${icon} Tool done: ${toolName}`, embed };
|
|
85
|
+
}
|
|
86
|
+
export function formatMessage(event) {
|
|
87
|
+
// Extract text from content blocks or message field
|
|
88
|
+
let text = '';
|
|
89
|
+
// Try content array first (most common for agent messages)
|
|
90
|
+
if (Array.isArray(event.content)) {
|
|
91
|
+
const blocks = event.content;
|
|
92
|
+
text = blocks
|
|
93
|
+
.filter((b) => b.type === 'text' && typeof b.text === 'string')
|
|
94
|
+
.map((b) => b.text)
|
|
95
|
+
.join('\n');
|
|
96
|
+
}
|
|
97
|
+
// Try message field — could be string, object with content array, or object with text
|
|
98
|
+
if (!text && event.message != null) {
|
|
99
|
+
if (typeof event.message === 'string') {
|
|
100
|
+
text = event.message;
|
|
101
|
+
}
|
|
102
|
+
else if (typeof event.message === 'object') {
|
|
103
|
+
const msg = event.message;
|
|
104
|
+
if (Array.isArray(msg.content)) {
|
|
105
|
+
const blocks = msg.content;
|
|
106
|
+
text = blocks
|
|
107
|
+
.filter((b) => b.type === 'text' && typeof b.text === 'string')
|
|
108
|
+
.map((b) => b.text)
|
|
109
|
+
.join('\n');
|
|
110
|
+
}
|
|
111
|
+
else if (typeof msg.text === 'string') {
|
|
112
|
+
text = msg.text;
|
|
113
|
+
}
|
|
114
|
+
else if (typeof msg.content === 'string') {
|
|
115
|
+
text = msg.content;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Fallback to text or content as plain strings
|
|
120
|
+
if (!text) {
|
|
121
|
+
text = typeof event.text === 'string' ? event.text : '';
|
|
122
|
+
}
|
|
123
|
+
if (!text && typeof event.content === 'string') {
|
|
124
|
+
text = event.content;
|
|
125
|
+
}
|
|
126
|
+
if (!text) {
|
|
127
|
+
return { content: '💬 (empty message)' };
|
|
128
|
+
}
|
|
129
|
+
const embed = new EmbedBuilder()
|
|
130
|
+
.setColor(COLOR.info)
|
|
131
|
+
.setDescription(truncate(text, 2000))
|
|
132
|
+
.setTimestamp();
|
|
133
|
+
const role = str(event.role);
|
|
134
|
+
if (role) {
|
|
135
|
+
embed.setAuthor({ name: role });
|
|
136
|
+
}
|
|
137
|
+
return { content: `💬 ${truncate(text, 200)}`, embed };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Format a blocker (extension_ui_request needing user response).
|
|
141
|
+
* Produces an embed with @mention and interactive buttons for select/confirm,
|
|
142
|
+
* or text instructions for input/editor.
|
|
143
|
+
*/
|
|
144
|
+
export function formatBlocker(blocker, ownerId) {
|
|
145
|
+
const mention = `<@${ownerId}>`;
|
|
146
|
+
const embed = new EmbedBuilder()
|
|
147
|
+
.setColor(COLOR.blocker)
|
|
148
|
+
.setTitle('⚠️ Blocker — Response Needed')
|
|
149
|
+
.setDescription(truncate(blocker.message, 2000))
|
|
150
|
+
.setTimestamp();
|
|
151
|
+
const components = [];
|
|
152
|
+
switch (blocker.method) {
|
|
153
|
+
case 'select': {
|
|
154
|
+
const evt = blocker.event;
|
|
155
|
+
const options = Array.isArray(evt.options) ? evt.options : [];
|
|
156
|
+
if (options.length > 0) {
|
|
157
|
+
// Discord ActionRow max 5 buttons, so chunk
|
|
158
|
+
const chunks = chunkArray(options.slice(0, 25), 5);
|
|
159
|
+
for (const chunk of chunks) {
|
|
160
|
+
const row = new ActionRowBuilder();
|
|
161
|
+
chunk.forEach((opt, i) => {
|
|
162
|
+
const globalIndex = options.indexOf(opt);
|
|
163
|
+
row.addComponents(new ButtonBuilder()
|
|
164
|
+
.setCustomId(`blocker:${blocker.id}:select:${globalIndex}`)
|
|
165
|
+
.setLabel(truncate(`${globalIndex + 1}. ${opt}`, 80))
|
|
166
|
+
.setStyle(ButtonStyle.Primary));
|
|
167
|
+
});
|
|
168
|
+
components.push(row);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
embed.addFields({
|
|
172
|
+
name: 'Options',
|
|
173
|
+
value: options.map((o, i) => `**${i + 1}.** ${truncate(o, 100)}`).join('\n') || 'No options',
|
|
174
|
+
});
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
case 'confirm': {
|
|
178
|
+
const row = new ActionRowBuilder().addComponents(new ButtonBuilder()
|
|
179
|
+
.setCustomId(`blocker:${blocker.id}:confirm:true`)
|
|
180
|
+
.setLabel('Yes')
|
|
181
|
+
.setStyle(ButtonStyle.Success), new ButtonBuilder()
|
|
182
|
+
.setCustomId(`blocker:${blocker.id}:confirm:false`)
|
|
183
|
+
.setLabel('No')
|
|
184
|
+
.setStyle(ButtonStyle.Danger));
|
|
185
|
+
components.push(row);
|
|
186
|
+
const msg = str(blocker.event.message);
|
|
187
|
+
if (msg) {
|
|
188
|
+
embed.addFields({ name: 'Details', value: truncate(msg, 1024) });
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
case 'input': {
|
|
193
|
+
const placeholder = str(blocker.event.placeholder);
|
|
194
|
+
embed.addFields({
|
|
195
|
+
name: 'How to respond',
|
|
196
|
+
value: `Reply in this channel with your answer.${placeholder ? `\n*Hint: ${placeholder}*` : ''}`,
|
|
197
|
+
});
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
case 'editor': {
|
|
201
|
+
const prefill = str(blocker.event.prefill);
|
|
202
|
+
embed.addFields({
|
|
203
|
+
name: 'How to respond',
|
|
204
|
+
value: 'Reply in this channel with the full text.' +
|
|
205
|
+
(prefill ? `\n\nCurrent value:\n\`\`\`\n${truncate(prefill, 500)}\n\`\`\`` : ''),
|
|
206
|
+
});
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
default: {
|
|
210
|
+
embed.addFields({
|
|
211
|
+
name: 'How to respond',
|
|
212
|
+
value: `Reply in this channel (method: ${blocker.method}).`,
|
|
213
|
+
});
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
content: `${mention} ⚠️ **Blocker** — ${truncate(blocker.message, 150)}`,
|
|
219
|
+
embed,
|
|
220
|
+
components: components.length > 0 ? components : undefined,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
export function formatCompletion(event) {
|
|
224
|
+
const status = str(event.status, 'completed');
|
|
225
|
+
const isError = status === 'error' || status === 'cancelled';
|
|
226
|
+
const color = isError ? COLOR.error : COLOR.success;
|
|
227
|
+
const icon = isError ? '⚠️' : '🏁';
|
|
228
|
+
const embed = new EmbedBuilder()
|
|
229
|
+
.setColor(color)
|
|
230
|
+
.setTitle(`${icon} Execution ${status}`)
|
|
231
|
+
.setTimestamp();
|
|
232
|
+
const reason = str(event.reason);
|
|
233
|
+
if (reason) {
|
|
234
|
+
embed.setDescription(truncate(reason, 2000));
|
|
235
|
+
}
|
|
236
|
+
// Include final stats if present
|
|
237
|
+
const stats = event.stats;
|
|
238
|
+
if (stats) {
|
|
239
|
+
const fields = [];
|
|
240
|
+
if (stats.cost != null)
|
|
241
|
+
fields.push(`Cost: ${formatCost(num(stats.cost))}`);
|
|
242
|
+
if (stats.tokens?.total != null)
|
|
243
|
+
fields.push(`Tokens: ${num(stats.tokens.total).toLocaleString()}`);
|
|
244
|
+
if (fields.length)
|
|
245
|
+
embed.addFields({ name: 'Summary', value: fields.join(' · ') });
|
|
246
|
+
}
|
|
247
|
+
return { content: `${icon} Execution ${status}`, embed };
|
|
248
|
+
}
|
|
249
|
+
export function formatError(sessionId, error) {
|
|
250
|
+
const embed = new EmbedBuilder()
|
|
251
|
+
.setColor(COLOR.error)
|
|
252
|
+
.setTitle('❌ Session Error')
|
|
253
|
+
.setDescription(`\`\`\`\n${truncate(error, 2000)}\n\`\`\``)
|
|
254
|
+
.setFooter({ text: `Session: ${sessionId}` })
|
|
255
|
+
.setTimestamp();
|
|
256
|
+
return { content: `❌ Error: ${truncate(error, 200)}`, embed };
|
|
257
|
+
}
|
|
258
|
+
export function formatCostUpdate(event) {
|
|
259
|
+
const cost = num(event.cumulativeCost ?? event.totalCost);
|
|
260
|
+
const tokens = event.tokens;
|
|
261
|
+
const embed = new EmbedBuilder()
|
|
262
|
+
.setColor(COLOR.info)
|
|
263
|
+
.setTitle('💰 Cost Update')
|
|
264
|
+
.setTimestamp();
|
|
265
|
+
const fields = [`Total: ${formatCost(cost)}`];
|
|
266
|
+
if (tokens) {
|
|
267
|
+
const input = num(tokens.input);
|
|
268
|
+
const output = num(tokens.output);
|
|
269
|
+
if (input || output) {
|
|
270
|
+
fields.push(`Tokens: ${input.toLocaleString()} in / ${output.toLocaleString()} out`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
embed.setDescription(fields.join('\n'));
|
|
274
|
+
return { content: `💰 Cost: ${formatCost(cost)}`, embed };
|
|
275
|
+
}
|
|
276
|
+
export function formatSessionStarted(projectName) {
|
|
277
|
+
const embed = new EmbedBuilder()
|
|
278
|
+
.setColor(COLOR.info)
|
|
279
|
+
.setTitle('🚀 Session Started')
|
|
280
|
+
.setDescription(`Project: **${truncate(projectName, 200)}**`)
|
|
281
|
+
.setTimestamp();
|
|
282
|
+
return { content: `🚀 Session started: ${projectName}`, embed };
|
|
283
|
+
}
|
|
284
|
+
export function formatTaskTransition(event) {
|
|
285
|
+
const taskId = str(event.taskId || event.task);
|
|
286
|
+
const sliceId = str(event.sliceId || event.slice);
|
|
287
|
+
const status = str(event.status || event.state);
|
|
288
|
+
const icon = status === 'complete' ? '✅' : status === 'error' ? '❌' : '📋';
|
|
289
|
+
const embed = new EmbedBuilder()
|
|
290
|
+
.setColor(status === 'complete' ? COLOR.success : status === 'error' ? COLOR.error : COLOR.info)
|
|
291
|
+
.setTitle(`${icon} Task Transition`)
|
|
292
|
+
.setTimestamp();
|
|
293
|
+
const fields = [];
|
|
294
|
+
if (sliceId)
|
|
295
|
+
fields.push(`Slice: ${sliceId}`);
|
|
296
|
+
if (taskId)
|
|
297
|
+
fields.push(`Task: ${taskId}`);
|
|
298
|
+
if (status)
|
|
299
|
+
fields.push(`Status: ${status}`);
|
|
300
|
+
embed.setDescription(fields.join('\n'));
|
|
301
|
+
return { content: `${icon} ${taskId || 'Task'} → ${status || 'unknown'}`, embed };
|
|
302
|
+
}
|
|
303
|
+
export function formatGenericEvent(event) {
|
|
304
|
+
const type = str(event.type, 'unknown');
|
|
305
|
+
const embed = new EmbedBuilder()
|
|
306
|
+
.setColor(COLOR.tool)
|
|
307
|
+
.setTitle(`📡 ${truncate(type, 60)}`)
|
|
308
|
+
.setTimestamp();
|
|
309
|
+
// Include a JSON preview of the event, stripping the type field
|
|
310
|
+
const { type: _t, ...rest } = event;
|
|
311
|
+
const preview = JSON.stringify(rest);
|
|
312
|
+
if (preview.length > 2) { // more than '{}'
|
|
313
|
+
embed.setDescription(`\`\`\`json\n${truncate(preview, 1000)}\n\`\`\``);
|
|
314
|
+
}
|
|
315
|
+
return { content: `📡 Event: ${type}`, embed };
|
|
316
|
+
}
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
// Dispatch — maps event type to the right formatter
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
/**
|
|
321
|
+
* Format any SdkAgentEvent for Discord. Falls back to formatGenericEvent
|
|
322
|
+
* for unknown types.
|
|
323
|
+
*/
|
|
324
|
+
export function formatEvent(event, ownerId) {
|
|
325
|
+
const type = str(event.type);
|
|
326
|
+
switch (type) {
|
|
327
|
+
case 'tool_execution_start':
|
|
328
|
+
return formatToolStart(event);
|
|
329
|
+
case 'tool_execution_end':
|
|
330
|
+
return formatToolEnd(event);
|
|
331
|
+
case 'message_start':
|
|
332
|
+
case 'message_end':
|
|
333
|
+
case 'message':
|
|
334
|
+
return formatMessage(event);
|
|
335
|
+
case 'execution_complete':
|
|
336
|
+
return formatCompletion(event);
|
|
337
|
+
case 'cost_update':
|
|
338
|
+
return formatCostUpdate(event);
|
|
339
|
+
case 'task_transition':
|
|
340
|
+
return formatTaskTransition(event);
|
|
341
|
+
default:
|
|
342
|
+
return formatGenericEvent(event);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// ---------------------------------------------------------------------------
|
|
346
|
+
// Utility
|
|
347
|
+
// ---------------------------------------------------------------------------
|
|
348
|
+
function chunkArray(arr, size) {
|
|
349
|
+
const chunks = [];
|
|
350
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
351
|
+
chunks.push(arr.slice(i, i + size));
|
|
352
|
+
}
|
|
353
|
+
return chunks;
|
|
354
|
+
}
|
|
355
|
+
//# sourceMappingURL=event-formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-formatter.js","sourceRoot":"","sources":["../src/event-formatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIxF,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E,MAAM,KAAK,GAAG;IACZ,OAAO,EAAE,QAAQ,EAAE,QAAQ;IAC3B,KAAK,EAAE,QAAQ,EAAI,MAAM;IACzB,OAAO,EAAE,QAAQ,EAAG,SAAS;IAC7B,IAAI,EAAE,QAAQ,EAAM,OAAO;IAC3B,IAAI,EAAE,QAAQ,EAAM,OAAO;CACnB,CAAC;AAEX,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,oEAAoE;AACpE,SAAS,QAAQ,CAAC,CAAS,EAAE,MAAc;IACzC,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;AACtC,CAAC;AAED,oDAAoD;AACpD,SAAS,GAAG,CAAC,KAAc,EAAE,QAAQ,GAAG,EAAE;IACxC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,QAAQ,CAAC;IACnC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,8BAA8B;AAC9B,SAAS,GAAG,CAAC,KAAc,EAAE,QAAQ,GAAG,CAAC;IACvC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,gDAAgD;AAChD,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,IAAI,GAAG,IAAI;QAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/B,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,MAAM,UAAU,eAAe,CAAC,KAAoB;IAClD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE;SAC7B,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;SACpB,QAAQ,CAAC,MAAM,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC;SACxC,YAAY,EAAE,CAAC;IAElB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,CAAC,cAAc,CAAC,WAAW,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,YAAY,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAoB;IAChD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC;IAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAEjC,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE;SAC7B,QAAQ,CAAC,KAAK,CAAC;SACf,QAAQ,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC;SAC7C,YAAY,EAAE,CAAC;IAElB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;IACjD,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,CAAC,cAAc,CAAC,WAAW,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;IACzD,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,eAAe,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAoB;IAChD,oDAAoD;IACpD,IAAI,IAAI,GAAG,EAAE,CAAC;IAEd,2DAA2D;IAC3D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAkD,CAAC;QACxE,IAAI,GAAG,MAAM;aACV,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;aAC9D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAK,CAAC;aACnB,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,sFAAsF;IACtF,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;QACnC,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACtC,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC;QACvB,CAAC;aAAM,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,KAAK,CAAC,OAAkC,CAAC;YACrD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/B,MAAM,MAAM,GAAG,GAAG,CAAC,OAAkD,CAAC;gBACtE,IAAI,GAAG,MAAM;qBACV,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;qBAC9D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAK,CAAC;qBACnB,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;iBAAM,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACxC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YAClB,CAAC;iBAAM,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAC3C,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IAED,+CAA+C;IAC/C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1D,CAAC;IACD,IAAI,CAAC,IAAI,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC/C,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE;SAC7B,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;SACpB,cAAc,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;SACpC,YAAY,EAAE,CAAC;IAElB,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,IAAI,EAAE,CAAC;QACT,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAuB,EACvB,OAAe;IAEf,MAAM,OAAO,GAAG,KAAK,OAAO,GAAG,CAAC;IAChC,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE;SAC7B,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;SACvB,QAAQ,CAAC,8BAA8B,CAAC;SACxC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;SAC/C,YAAY,EAAE,CAAC;IAElB,MAAM,UAAU,GAAsC,EAAE,CAAC;IAEzD,QAAQ,OAAO,CAAC,MAAM,EAAE,CAAC;QACvB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,GAAG,GAAG,OAAO,CAAC,KAA+B,CAAC;YACpD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YAE9D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,4CAA4C;gBAC5C,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBACnD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;oBAC3B,MAAM,GAAG,GAAG,IAAI,gBAAgB,EAAiB,CAAC;oBAClD,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;wBACvB,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;wBACzC,GAAG,CAAC,aAAa,CACf,IAAI,aAAa,EAAE;6BAChB,WAAW,CAAC,WAAW,OAAO,CAAC,EAAE,WAAW,WAAW,EAAE,CAAC;6BAC1D,QAAQ,CAAC,QAAQ,CAAC,GAAG,WAAW,GAAG,CAAC,KAAK,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;6BACpD,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,CACjC,CAAC;oBACJ,CAAC,CAAC,CAAC;oBACH,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;YAED,KAAK,CAAC,SAAS,CAAC;gBACd,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,YAAY;aAC7F,CAAC,CAAC;YACH,MAAM;QACR,CAAC;QAED,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,GAAG,GAAG,IAAI,gBAAgB,EAAiB,CAAC,aAAa,CAC7D,IAAI,aAAa,EAAE;iBAChB,WAAW,CAAC,WAAW,OAAO,CAAC,EAAE,eAAe,CAAC;iBACjD,QAAQ,CAAC,KAAK,CAAC;iBACf,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,EAChC,IAAI,aAAa,EAAE;iBAChB,WAAW,CAAC,WAAW,OAAO,CAAC,EAAE,gBAAgB,CAAC;iBAClD,QAAQ,CAAC,IAAI,CAAC;iBACd,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAChC,CAAC;YACF,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAErB,MAAM,GAAG,GAAG,GAAG,CAAE,OAAO,CAAC,KAA8B,CAAC,OAAO,CAAC,CAAC;YACjE,IAAI,GAAG,EAAE,CAAC;gBACR,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;YACnE,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,WAAW,GAAG,GAAG,CAAE,OAAO,CAAC,KAAkC,CAAC,WAAW,CAAC,CAAC;YACjF,KAAK,CAAC,SAAS,CAAC;gBACd,IAAI,EAAE,gBAAgB;gBACtB,KAAK,EAAE,0CAA0C,WAAW,CAAC,CAAC,CAAC,YAAY,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;aACjG,CAAC,CAAC;YACH,MAAM;QACR,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,OAAO,GAAG,GAAG,CAAE,OAAO,CAAC,KAA8B,CAAC,OAAO,CAAC,CAAC;YACrE,KAAK,CAAC,SAAS,CAAC;gBACd,IAAI,EAAE,gBAAgB;gBACtB,KAAK,EAAE,2CAA2C;oBAChD,CAAC,OAAO,CAAC,CAAC,CAAC,+BAA+B,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;aACnF,CAAC,CAAC;YACH,MAAM;QACR,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,KAAK,CAAC,SAAS,CAAC;gBACd,IAAI,EAAE,gBAAgB;gBACtB,KAAK,EAAE,kCAAkC,OAAO,CAAC,MAAM,IAAI;aAC5D,CAAC,CAAC;YACH,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,EAAE,GAAG,OAAO,qBAAqB,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;QACxE,KAAK;QACL,UAAU,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;KAC3D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAoB;IACnD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,WAAW,CAAC;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IACpD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAEnC,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE;SAC7B,QAAQ,CAAC,KAAK,CAAC;SACf,QAAQ,CAAC,GAAG,IAAI,cAAc,MAAM,EAAE,CAAC;SACvC,YAAY,EAAE,CAAC;IAElB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,iCAAiC;IACjC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAmE,CAAC;IACxF,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,IAAI,IAAI;YAAE,MAAM,CAAC,IAAI,CAAC,SAAS,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5E,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,IAAI;YAAE,MAAM,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QACpG,IAAI,MAAM,CAAC,MAAM;YAAE,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,cAAc,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,KAAa;IAC1D,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE;SAC7B,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;SACrB,QAAQ,CAAC,iBAAiB,CAAC;SAC3B,cAAc,CAAC,WAAW,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC;SAC1D,SAAS,CAAC,EAAE,IAAI,EAAE,YAAY,SAAS,EAAE,EAAE,CAAC;SAC5C,YAAY,EAAE,CAAC;IAElB,OAAO,EAAE,OAAO,EAAE,YAAY,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAoB;IACnD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,MAER,CAAC;IAEd,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE;SAC7B,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;SACpB,QAAQ,CAAC,gBAAgB,CAAC;SAC1B,YAAY,EAAE,CAAC;IAElB,MAAM,MAAM,GAAa,CAAC,UAAU,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,WAAW,KAAK,CAAC,cAAc,EAAE,SAAS,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAExC,OAAO,EAAE,OAAO,EAAE,YAAY,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE;SAC7B,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;SACpB,QAAQ,CAAC,oBAAoB,CAAC;SAC9B,cAAc,CAAC,cAAc,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,IAAI,CAAC;SAC5D,YAAY,EAAE,CAAC;IAElB,OAAO,EAAE,OAAO,EAAE,uBAAuB,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAoB;IACvD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAE3E,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE;SAC7B,QAAQ,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;SAC/F,QAAQ,CAAC,GAAG,IAAI,kBAAkB,CAAC;SACnC,YAAY,EAAE,CAAC;IAElB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,OAAO;QAAE,MAAM,CAAC,IAAI,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;IAC9C,IAAI,MAAM;QAAE,MAAM,CAAC,IAAI,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAC3C,IAAI,MAAM;QAAE,MAAM,CAAC,IAAI,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC;IAC7C,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAExC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,IAAI,MAAM,IAAI,MAAM,MAAM,MAAM,IAAI,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAoB;IACrD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE;SAC7B,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;SACpB,QAAQ,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;SACpC,YAAY,EAAE,CAAC;IAElB,gEAAgE;IAChE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,iBAAiB;QACzC,KAAK,CAAC,cAAc,CAAC,eAAe,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,aAAa,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC;AACjD,CAAC;AAED,8EAA8E;AAC9E,oDAAoD;AACpD,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAoB,EAAE,OAAgB;IAChE,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE7B,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,sBAAsB;YACzB,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,KAAK,oBAAoB;YACvB,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,KAAK,eAAe,CAAC;QACrB,KAAK,aAAa,CAAC;QACnB,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,KAAK,oBAAoB;YACvB,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACjC,KAAK,aAAa;YAChB,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACjC,KAAK,iBAAiB;YACpB,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC;QACrC;YACE,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,SAAS,UAAU,CAAI,GAAQ,EAAE,IAAY;IAC3C,MAAM,MAAM,GAAU,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * event-formatter.ts — Pure functions mapping RPC event types to Discord embeds.\n *\n * Each formatter returns a FormattedEvent (content string + optional EmbedBuilder +\n * optional ActionRow components). Distinct embed colors per category:\n * green = success / completion\n * red = error\n * yellow = blocker (needs attention)\n * blue = info / session lifecycle\n * grey = tool / generic\n */\n\nimport { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';\nimport type { RpcExtensionUIRequest, SdkAgentEvent } from '@opengsd/contracts';\nimport type { FormattedEvent, PendingBlocker } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Color palette\n// ---------------------------------------------------------------------------\n\nconst COLOR = {\n success: 0x2ecc71, // green\n error: 0xe74c3c, // red\n blocker: 0xf1c40f, // yellow\n info: 0x3498db, // blue\n tool: 0x95a5a6, // grey\n} as const;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Truncate a string to maxLen, appending ellipsis if truncated. */\nfunction truncate(s: string, maxLen: number): string {\n if (s.length <= maxLen) return s;\n return s.slice(0, maxLen - 1) + '…';\n}\n\n/** Safe string extraction from an unknown field. */\nfunction str(value: unknown, fallback = ''): string {\n if (typeof value === 'string') return value;\n if (value == null) return fallback;\n return String(value);\n}\n\n/** Safe number extraction. */\nfunction num(value: unknown, fallback = 0): number {\n if (typeof value === 'number' && !Number.isNaN(value)) return value;\n return fallback;\n}\n\n/** Format a cost value to a readable string. */\nfunction formatCost(cost: number): string {\n if (cost < 0.01) return `$${cost.toFixed(4)}`;\n return `$${cost.toFixed(2)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Formatters\n// ---------------------------------------------------------------------------\n\nexport function formatToolStart(event: SdkAgentEvent): FormattedEvent {\n const toolName = str(event.name || event.toolName, 'unknown');\n const embed = new EmbedBuilder()\n .setColor(COLOR.tool)\n .setTitle(`🔧 ${truncate(toolName, 60)}`)\n .setTimestamp();\n\n const input = str(event.input || event.args);\n if (input) {\n embed.setDescription(`\\`\\`\\`\\n${truncate(input, 300)}\\n\\`\\`\\``);\n }\n\n return { content: `🔧 Tool: ${toolName}`, embed };\n}\n\nexport function formatToolEnd(event: SdkAgentEvent): FormattedEvent {\n const toolName = str(event.name || event.toolName, 'unknown');\n const isError = event.isError === true || event.error != null;\n const color = isError ? COLOR.error : COLOR.tool;\n const icon = isError ? '❌' : '✅';\n\n const embed = new EmbedBuilder()\n .setColor(color)\n .setTitle(`${icon} ${truncate(toolName, 60)}`)\n .setTimestamp();\n\n const output = str(event.output || event.result);\n if (output) {\n embed.setDescription(`\\`\\`\\`\\n${truncate(output, 300)}\\n\\`\\`\\``);\n }\n\n const duration = num(event.duration || event.durationMs);\n if (duration > 0) {\n embed.setFooter({ text: `${(duration / 1000).toFixed(1)}s` });\n }\n\n return { content: `${icon} Tool done: ${toolName}`, embed };\n}\n\nexport function formatMessage(event: SdkAgentEvent): FormattedEvent {\n // Extract text from content blocks or message field\n let text = '';\n\n // Try content array first (most common for agent messages)\n if (Array.isArray(event.content)) {\n const blocks = event.content as Array<{ type?: string; text?: string }>;\n text = blocks\n .filter((b) => b.type === 'text' && typeof b.text === 'string')\n .map((b) => b.text!)\n .join('\\n');\n }\n\n // Try message field — could be string, object with content array, or object with text\n if (!text && event.message != null) {\n if (typeof event.message === 'string') {\n text = event.message;\n } else if (typeof event.message === 'object') {\n const msg = event.message as Record<string, unknown>;\n if (Array.isArray(msg.content)) {\n const blocks = msg.content as Array<{ type?: string; text?: string }>;\n text = blocks\n .filter((b) => b.type === 'text' && typeof b.text === 'string')\n .map((b) => b.text!)\n .join('\\n');\n } else if (typeof msg.text === 'string') {\n text = msg.text;\n } else if (typeof msg.content === 'string') {\n text = msg.content;\n }\n }\n }\n\n // Fallback to text or content as plain strings\n if (!text) {\n text = typeof event.text === 'string' ? event.text : '';\n }\n if (!text && typeof event.content === 'string') {\n text = event.content;\n }\n\n if (!text) {\n return { content: '💬 (empty message)' };\n }\n\n const embed = new EmbedBuilder()\n .setColor(COLOR.info)\n .setDescription(truncate(text, 2000))\n .setTimestamp();\n\n const role = str(event.role);\n if (role) {\n embed.setAuthor({ name: role });\n }\n\n return { content: `💬 ${truncate(text, 200)}`, embed };\n}\n\n/**\n * Format a blocker (extension_ui_request needing user response).\n * Produces an embed with @mention and interactive buttons for select/confirm,\n * or text instructions for input/editor.\n */\nexport function formatBlocker(\n blocker: PendingBlocker,\n ownerId: string,\n): FormattedEvent {\n const mention = `<@${ownerId}>`;\n const embed = new EmbedBuilder()\n .setColor(COLOR.blocker)\n .setTitle('⚠️ Blocker — Response Needed')\n .setDescription(truncate(blocker.message, 2000))\n .setTimestamp();\n\n const components: ActionRowBuilder<ButtonBuilder>[] = [];\n\n switch (blocker.method) {\n case 'select': {\n const evt = blocker.event as { options?: string[] };\n const options = Array.isArray(evt.options) ? evt.options : [];\n\n if (options.length > 0) {\n // Discord ActionRow max 5 buttons, so chunk\n const chunks = chunkArray(options.slice(0, 25), 5);\n for (const chunk of chunks) {\n const row = new ActionRowBuilder<ButtonBuilder>();\n chunk.forEach((opt, i) => {\n const globalIndex = options.indexOf(opt);\n row.addComponents(\n new ButtonBuilder()\n .setCustomId(`blocker:${blocker.id}:select:${globalIndex}`)\n .setLabel(truncate(`${globalIndex + 1}. ${opt}`, 80))\n .setStyle(ButtonStyle.Primary),\n );\n });\n components.push(row);\n }\n }\n\n embed.addFields({\n name: 'Options',\n value: options.map((o, i) => `**${i + 1}.** ${truncate(o, 100)}`).join('\\n') || 'No options',\n });\n break;\n }\n\n case 'confirm': {\n const row = new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder()\n .setCustomId(`blocker:${blocker.id}:confirm:true`)\n .setLabel('Yes')\n .setStyle(ButtonStyle.Success),\n new ButtonBuilder()\n .setCustomId(`blocker:${blocker.id}:confirm:false`)\n .setLabel('No')\n .setStyle(ButtonStyle.Danger),\n );\n components.push(row);\n\n const msg = str((blocker.event as { message?: string }).message);\n if (msg) {\n embed.addFields({ name: 'Details', value: truncate(msg, 1024) });\n }\n break;\n }\n\n case 'input': {\n const placeholder = str((blocker.event as { placeholder?: string }).placeholder);\n embed.addFields({\n name: 'How to respond',\n value: `Reply in this channel with your answer.${placeholder ? `\\n*Hint: ${placeholder}*` : ''}`,\n });\n break;\n }\n\n case 'editor': {\n const prefill = str((blocker.event as { prefill?: string }).prefill);\n embed.addFields({\n name: 'How to respond',\n value: 'Reply in this channel with the full text.' +\n (prefill ? `\\n\\nCurrent value:\\n\\`\\`\\`\\n${truncate(prefill, 500)}\\n\\`\\`\\`` : ''),\n });\n break;\n }\n\n default: {\n embed.addFields({\n name: 'How to respond',\n value: `Reply in this channel (method: ${blocker.method}).`,\n });\n break;\n }\n }\n\n return {\n content: `${mention} ⚠️ **Blocker** — ${truncate(blocker.message, 150)}`,\n embed,\n components: components.length > 0 ? components : undefined,\n };\n}\n\nexport function formatCompletion(event: SdkAgentEvent): FormattedEvent {\n const status = str(event.status, 'completed');\n const isError = status === 'error' || status === 'cancelled';\n const color = isError ? COLOR.error : COLOR.success;\n const icon = isError ? '⚠️' : '🏁';\n\n const embed = new EmbedBuilder()\n .setColor(color)\n .setTitle(`${icon} Execution ${status}`)\n .setTimestamp();\n\n const reason = str(event.reason);\n if (reason) {\n embed.setDescription(truncate(reason, 2000));\n }\n\n // Include final stats if present\n const stats = event.stats as { cost?: number; tokens?: { total?: number } } | undefined;\n if (stats) {\n const fields: string[] = [];\n if (stats.cost != null) fields.push(`Cost: ${formatCost(num(stats.cost))}`);\n if (stats.tokens?.total != null) fields.push(`Tokens: ${num(stats.tokens.total).toLocaleString()}`);\n if (fields.length) embed.addFields({ name: 'Summary', value: fields.join(' · ') });\n }\n\n return { content: `${icon} Execution ${status}`, embed };\n}\n\nexport function formatError(sessionId: string, error: string): FormattedEvent {\n const embed = new EmbedBuilder()\n .setColor(COLOR.error)\n .setTitle('❌ Session Error')\n .setDescription(`\\`\\`\\`\\n${truncate(error, 2000)}\\n\\`\\`\\``)\n .setFooter({ text: `Session: ${sessionId}` })\n .setTimestamp();\n\n return { content: `❌ Error: ${truncate(error, 200)}`, embed };\n}\n\nexport function formatCostUpdate(event: SdkAgentEvent): FormattedEvent {\n const cost = num(event.cumulativeCost ?? event.totalCost);\n const tokens = event.tokens as\n | { input?: number; output?: number; cacheRead?: number; cacheWrite?: number }\n | undefined;\n\n const embed = new EmbedBuilder()\n .setColor(COLOR.info)\n .setTitle('💰 Cost Update')\n .setTimestamp();\n\n const fields: string[] = [`Total: ${formatCost(cost)}`];\n if (tokens) {\n const input = num(tokens.input);\n const output = num(tokens.output);\n if (input || output) {\n fields.push(`Tokens: ${input.toLocaleString()} in / ${output.toLocaleString()} out`);\n }\n }\n embed.setDescription(fields.join('\\n'));\n\n return { content: `💰 Cost: ${formatCost(cost)}`, embed };\n}\n\nexport function formatSessionStarted(projectName: string): FormattedEvent {\n const embed = new EmbedBuilder()\n .setColor(COLOR.info)\n .setTitle('🚀 Session Started')\n .setDescription(`Project: **${truncate(projectName, 200)}**`)\n .setTimestamp();\n\n return { content: `🚀 Session started: ${projectName}`, embed };\n}\n\nexport function formatTaskTransition(event: SdkAgentEvent): FormattedEvent {\n const taskId = str(event.taskId || event.task);\n const sliceId = str(event.sliceId || event.slice);\n const status = str(event.status || event.state);\n const icon = status === 'complete' ? '✅' : status === 'error' ? '❌' : '📋';\n\n const embed = new EmbedBuilder()\n .setColor(status === 'complete' ? COLOR.success : status === 'error' ? COLOR.error : COLOR.info)\n .setTitle(`${icon} Task Transition`)\n .setTimestamp();\n\n const fields: string[] = [];\n if (sliceId) fields.push(`Slice: ${sliceId}`);\n if (taskId) fields.push(`Task: ${taskId}`);\n if (status) fields.push(`Status: ${status}`);\n embed.setDescription(fields.join('\\n'));\n\n return { content: `${icon} ${taskId || 'Task'} → ${status || 'unknown'}`, embed };\n}\n\nexport function formatGenericEvent(event: SdkAgentEvent): FormattedEvent {\n const type = str(event.type, 'unknown');\n const embed = new EmbedBuilder()\n .setColor(COLOR.tool)\n .setTitle(`📡 ${truncate(type, 60)}`)\n .setTimestamp();\n\n // Include a JSON preview of the event, stripping the type field\n const { type: _t, ...rest } = event;\n const preview = JSON.stringify(rest);\n if (preview.length > 2) { // more than '{}'\n embed.setDescription(`\\`\\`\\`json\\n${truncate(preview, 1000)}\\n\\`\\`\\``);\n }\n\n return { content: `📡 Event: ${type}`, embed };\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch — maps event type to the right formatter\n// ---------------------------------------------------------------------------\n\n/**\n * Format any SdkAgentEvent for Discord. Falls back to formatGenericEvent\n * for unknown types.\n */\nexport function formatEvent(event: SdkAgentEvent, ownerId?: string): FormattedEvent {\n const type = str(event.type);\n\n switch (type) {\n case 'tool_execution_start':\n return formatToolStart(event);\n case 'tool_execution_end':\n return formatToolEnd(event);\n case 'message_start':\n case 'message_end':\n case 'message':\n return formatMessage(event);\n case 'execution_complete':\n return formatCompletion(event);\n case 'cost_update':\n return formatCostUpdate(event);\n case 'task_transition':\n return formatTaskTransition(event);\n default:\n return formatGenericEvent(event);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Utility\n// ---------------------------------------------------------------------------\n\nfunction chunkArray<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size));\n }\n return chunks;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-formatter.test.d.ts","sourceRoot":"","sources":["../src/event-formatter.test.ts"],"names":[],"mappings":""}
|