@memberjunction/ng-conversations 6.2.0-edge.5 → 6.2.0-edge.7
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/lib/components/attachment/image-viewer.component.d.ts +3 -1
- package/dist/lib/components/attachment/image-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/attachment/image-viewer.component.js +5 -1
- package/dist/lib/components/attachment/image-viewer.component.js.map +1 -1
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +1 -0
- package/dist/lib/components/collection/collection-view.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +188 -9
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +1333 -464
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-mode-picker.component.js +2 -2
- package/dist/lib/components/conversation/conversation-mode-picker.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-remote-browser-controller.d.ts +109 -0
- package/dist/lib/components/conversation/conversation-remote-browser-controller.d.ts.map +1 -0
- package/dist/lib/components/conversation/conversation-remote-browser-controller.js +298 -0
- package/dist/lib/components/conversation/conversation-remote-browser-controller.js.map +1 -0
- package/dist/lib/components/message/agent-handoff-card.component.d.ts +102 -0
- package/dist/lib/components/message/agent-handoff-card.component.d.ts.map +1 -0
- package/dist/lib/components/message/agent-handoff-card.component.js +439 -0
- package/dist/lib/components/message/agent-handoff-card.component.js.map +1 -0
- package/dist/lib/components/message/message-input.component.d.ts +126 -59
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +559 -554
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +34 -2
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +678 -520
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +13 -1
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +92 -39
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts +19 -0
- package/dist/lib/components/overlay/chat-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.js +125 -13
- package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
- package/dist/lib/components/project/project-form-modal.component.js +2 -2
- package/dist/lib/components/project/project-form-modal.component.js.map +1 -1
- package/dist/lib/components/realtime/interactive-component/run-view-component-artifact-source.js +1 -1
- package/dist/lib/components/realtime/interactive-component/run-view-component-artifact-source.js.map +1 -1
- package/dist/lib/components/realtime/media/media-channel-state.d.ts +5 -0
- package/dist/lib/components/realtime/media/media-channel-state.d.ts.map +1 -1
- package/dist/lib/components/realtime/media/media-channel-state.js +27 -1
- package/dist/lib/components/realtime/media/media-channel-state.js.map +1 -1
- package/dist/lib/components/realtime/media/media-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/media/media-channel.js +7 -2
- package/dist/lib/components/realtime/media/media-channel.js.map +1 -1
- package/dist/lib/components/realtime/media/realtime-media-surface.component.d.ts +19 -2
- package/dist/lib/components/realtime/media/realtime-media-surface.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js +59 -10
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +22 -6
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.js +160 -98
- package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-call-failure-notice.d.ts +13 -0
- package/dist/lib/components/realtime/realtime-call-failure-notice.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-call-failure-notice.js +21 -0
- package/dist/lib/components/realtime/realtime-call-failure-notice.js.map +1 -0
- package/dist/lib/components/realtime/realtime-composer.component.d.ts +26 -3
- package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.js +226 -120
- package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +73 -16
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.js +219 -108
- package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.d.ts +9 -12
- package/dist/lib/components/realtime/realtime-ui-config.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.js +38 -18
- package/dist/lib/components/realtime/realtime-ui-config.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +34 -5
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts +8 -2
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js +51 -10
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js.map +1 -1
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel-config.d.ts +46 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel-config.d.ts.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel-config.js +64 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel-config.js.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +131 -9
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +347 -38
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +2 -1
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +9 -4
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/events/chat-events.d.ts +5 -4
- package/dist/lib/events/chat-events.d.ts.map +1 -1
- package/dist/lib/events/chat-events.js +2 -2
- package/dist/lib/events/chat-events.js.map +1 -1
- package/dist/lib/models/agent-turn.model.d.ts +11 -7
- package/dist/lib/models/agent-turn.model.d.ts.map +1 -1
- package/dist/lib/models/agent-turn.model.js.map +1 -1
- package/dist/lib/services/active-tasks.service.d.ts +5 -0
- package/dist/lib/services/active-tasks.service.d.ts.map +1 -1
- package/dist/lib/services/active-tasks.service.js +16 -0
- package/dist/lib/services/active-tasks.service.js.map +1 -1
- package/dist/lib/services/agent-state.service.d.ts.map +1 -1
- package/dist/lib/services/agent-state.service.js +7 -1
- package/dist/lib/services/agent-state.service.js.map +1 -1
- package/dist/lib/services/artifact-permission.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-permission.service.js +2 -0
- package/dist/lib/services/artifact-permission.service.js.map +1 -1
- package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-state.service.js +1 -0
- package/dist/lib/services/artifact-state.service.js.map +1 -1
- package/dist/lib/services/collection-permission.service.d.ts.map +1 -1
- package/dist/lib/services/collection-permission.service.js +1 -0
- package/dist/lib/services/collection-permission.service.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +38 -16
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +57 -71
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-detail-window.store.d.ts +5 -0
- package/dist/lib/services/conversation-detail-window.store.d.ts.map +1 -1
- package/dist/lib/services/conversation-detail-window.store.js +52 -5
- package/dist/lib/services/conversation-detail-window.store.js.map +1 -1
- package/dist/lib/services/realtime-session-review.service.d.ts +2 -1
- package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session-review.service.js.map +1 -1
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session.service.js +2 -1
- package/dist/lib/services/realtime-session.service.js.map +1 -1
- package/dist/lib/services/search.service.d.ts.map +1 -1
- package/dist/lib/services/search.service.js +1 -0
- package/dist/lib/services/search.service.js.map +1 -1
- package/dist/lib/utils/agent-failure-display.d.ts +14 -0
- package/dist/lib/utils/agent-failure-display.d.ts.map +1 -0
- package/dist/lib/utils/agent-failure-display.js +26 -0
- package/dist/lib/utils/agent-failure-display.js.map +1 -0
- package/dist/lib/utils/agent-handoff-map.d.ts +52 -0
- package/dist/lib/utils/agent-handoff-map.d.ts.map +1 -0
- package/dist/lib/utils/agent-handoff-map.js +270 -0
- package/dist/lib/utils/agent-handoff-map.js.map +1 -0
- package/dist/lib/utils/agent-turn-routing.d.ts +62 -7
- package/dist/lib/utils/agent-turn-routing.d.ts.map +1 -1
- package/dist/lib/utils/agent-turn-routing.js +147 -6
- package/dist/lib/utils/agent-turn-routing.js.map +1 -1
- package/dist/lib/utils/decision-routing.d.ts +17 -9
- package/dist/lib/utils/decision-routing.d.ts.map +1 -1
- package/dist/lib/utils/decision-routing.js +23 -14
- package/dist/lib/utils/decision-routing.js.map +1 -1
- package/dist/lib/utils/message-list-order.d.ts +13 -0
- package/dist/lib/utils/message-list-order.d.ts.map +1 -0
- package/dist/lib/utils/message-list-order.js +31 -0
- package/dist/lib/utils/message-list-order.js.map +1 -0
- package/dist/lib/utils/pin-state.d.ts +11 -0
- package/dist/lib/utils/pin-state.d.ts.map +1 -0
- package/dist/lib/utils/pin-state.js +9 -0
- package/dist/lib/utils/pin-state.js.map +1 -0
- package/dist/lib/utils/thread-replies.d.ts +34 -0
- package/dist/lib/utils/thread-replies.d.ts.map +1 -0
- package/dist/lib/utils/thread-replies.js +49 -0
- package/dist/lib/utils/thread-replies.js.map +1 -0
- package/dist/public-api.d.ts +6 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +6 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +36 -36
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-turn-routing.js","sourceRoot":"","sources":["../../../src/lib/utils/agent-turn-routing.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Pure routing rules for an agent turn: which agent answers a message, and whether\n * any does. Kept free of Angular and of I/O so the whole matrix (reply mode × mention × allowed\n * list × route) is testable directly; `MessageInputComponent` supplies the candidates.\n *\n * @module @memberjunction/ng-conversations\n */\n\nimport { IsAgentAllowed } from '@memberjunction/ai-core-plus';\nimport type { AgentReplyMode, AgentTurnRoute, AgentTurnTarget } from '../models/agent-turn.model';\n\n/** The agent each route would use for one message, before the host's rules are applied. */\nexport interface AgentTurnCandidates {\n /** Agents the message tags, in the order they appear. */\n MentionedAgentIds: readonly string[];\n /** The last agent that answered in the conversation, other than the conversation manager. */\n ContinuityAgentId: string | null;\n /**\n * True when a routing decision put `ContinuityAgentId` there in place of the last agent that\n * answered. The turn is then labelled `DecisionRouted` instead of `Continuity`.\n */\n ContinuityDecisionRouted?: boolean;\n /** The agent pinned on the conversation. */\n ConversationDefaultAgentId: string | null;\n /** The host's `DefaultAgentId` input. */\n HostDefaultAgentId: string | null;\n /** MJ's conversation manager agent. */\n ConversationManagerAgentId: string | null;\n}\n\n/** The host's rules for the chat. */\nexport interface AgentTurnRules {\n /** When a message starts a turn. */\n ReplyMode: AgentReplyMode;\n /** The agents that may answer. Null allows every agent. */\n AllowedAgentIDs: readonly string[] | null;\n}\n\n/**\n * Picks the agent and route for one message, or null when no turn starts.\n *\n * The order is MJ's: the first tagged agent the host allows, then (under `Always` only) the last\n * agent that answered, the conversation's pinned agent, the host's default agent, and the\n * conversation manager. A candidate the host doesn't allow is skipped. A continuity candidate that\n * a routing decision chose (`ContinuityDecisionRouted`) takes the `DecisionRouted` label.\n *\n * A continuity or default candidate that `isKnownAgent` doesn't recognise falls back to the\n * conversation manager, as MJ always has, rather than to the next route. A tagged agent is taken\n * as tagged.\n *\n * @param candidates The agent each route would use.\n * @param rules The host's reply mode and allowed list.\n * @param isKnownAgent True when the agent exists in the client's agent catalog.\n */\nexport function ResolveAgentTurn(\n candidates: AgentTurnCandidates,\n rules: AgentTurnRules,\n isKnownAgent: (agentId: string) => boolean\n): AgentTurnTarget | null {\n const mentioned = candidates.MentionedAgentIds.find(id => IsAgentAllowed(id, rules.AllowedAgentIDs));\n if (mentioned) {\n return { AgentId: mentioned, Route: 'Mention' };\n }\n if (rules.ReplyMode === 'MentionOnly') {\n return null;\n }\n\n const implicitRoutes: ReadonlyArray<[AgentTurnRoute, string | null]> = [\n [candidates.ContinuityDecisionRouted ? 'DecisionRouted' : 'Continuity', candidates.ContinuityAgentId],\n ['ConversationDefault', candidates.ConversationDefaultAgentId],\n ['HostDefault', candidates.HostDefaultAgentId],\n ];\n for (const [route, agentId] of implicitRoutes) {\n if (agentId && IsAgentAllowed(agentId, rules.AllowedAgentIDs)) {\n return isKnownAgent(agentId)\n ? { AgentId: agentId, Route: route }\n : conversationManagerTarget(candidates, rules);\n }\n }\n return conversationManagerTarget(candidates, rules);\n}\n\n/** The conversation manager's turn, when there is one and the host allows it. */\nfunction conversationManagerTarget(candidates: AgentTurnCandidates, rules: AgentTurnRules): AgentTurnTarget | null {\n const agentId = candidates.ConversationManagerAgentId;\n return agentId && IsAgentAllowed(agentId, rules.AllowedAgentIDs)\n ? { AgentId: agentId, Route: 'ConversationManager' }\n : null;\n}\n\n/** A task-graph step that runs an agent, as the conversation manager emits it. */\ninterface AgentTaskStep {\n kind: string;\n configuration?: { agentName?: string } | object;\n}\n\n/**\n * The agent names in a task graph that the host doesn't allow. The conversation manager can\n * answer with a multi-step workflow instead of a single delegation; each `Agent` step names the\n * agent that runs it, so the allowed list has to hold there too.\n *\n * @param steps The graph's tasks.\n * @param allowedAgentIDs The host's allowed list. Null allows every agent.\n * @param resolveAgentId Maps an agent name to its ID, or null when no such agent exists.\n * @returns The disallowed names, each once, in the order they appear. Empty when all are allowed.\n */\nexport function FindDisallowedTaskGraphAgents(\n steps: ReadonlyArray<AgentTaskStep>,\n allowedAgentIDs: readonly string[] | null,\n resolveAgentId: (agentName: string) => string | null\n): string[] {\n if (allowedAgentIDs == null) {\n return [];\n }\n const disallowed: string[] = [];\n for (const step of steps) {\n const agentName = step.kind === 'Agent' ? agentNameOf(step.configuration) : null;\n if (agentName && !IsAgentAllowed(resolveAgentId(agentName), allowedAgentIDs) && !disallowed.includes(agentName)) {\n disallowed.push(agentName);\n }\n }\n return disallowed;\n}\n\n/** The `agentName` of an Agent step's configuration, when it has one. */\nfunction agentNameOf(configuration: AgentTaskStep['configuration']): string | null {\n if (configuration && 'agentName' in configuration && typeof configuration.agentName === 'string') {\n return configuration.agentName;\n }\n return null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent-turn-routing.js","sourceRoot":"","sources":["../../../src/lib/utils/agent-turn-routing.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Pure routing rules for an agent turn: which agent answers a message, and whether\n * any does. Kept free of Angular and of I/O so the whole matrix (reply mode × mention × allowed\n * list × route) is testable directly; `MessageInputComponent` supplies the candidates.\n *\n * @module @memberjunction/ng-conversations\n */\n\nimport { IsAgentAllowed } from '@memberjunction/ai-core-plus';\nimport { NormalizeUUID, UUIDsEqual } from '@memberjunction/global';\nimport type { AgentReplyMode, AgentTurnRoute, AgentTurnTarget } from '../models/agent-turn.model';\n\n/** The agent each route would use for one message, before the host's rules are applied. */\nexport interface AgentTurnCandidates {\n /** Agents the message tags, in the order they appear. */\n MentionedAgentIds: readonly string[];\n /** People the message tags (`@Person`). Addressing a person with no agent tag starts no agent turn. */\n MentionedUserIds?: readonly string[];\n /** The agent of the message this one replies to (`ParentID` -> `Role === 'AI'`), when set. */\n ReplyParentAgentId?: string | null;\n /** True when this message replies (`ParentID`) to another person's message (`Role === 'User'`). */\n ReplyParentIsHuman?: boolean;\n /** The last agent that answered in the conversation, other than the conversation manager. */\n ContinuityAgentId: string | null;\n /**\n * True when a routing decision put `ContinuityAgentId` there in place of the last agent that\n * answered (or confirmed it in a multi-person chat). The turn is then labelled `DecisionRouted`\n * instead of `Continuity`.\n */\n ContinuityDecisionRouted?: boolean;\n /** True when a routing decision in a multi-person chat concluded the message is addressed to a person. */\n DecisionAddressedToHuman?: boolean;\n /** The agent pinned on the conversation. */\n ConversationDefaultAgentId: string | null;\n /** The host's `DefaultAgentId` input. */\n HostDefaultAgentId: string | null;\n /** MJ's conversation manager agent. */\n ConversationManagerAgentId: string | null;\n}\n\n/** The host's rules for the chat. */\nexport interface AgentTurnRules {\n /** When a message starts a turn. */\n ReplyMode: AgentReplyMode;\n /** The agents that may answer. Null allows every agent. */\n AllowedAgentIDs: readonly string[] | null;\n}\n\n/**\n * True when a message with no allowed `@Agent` mention is intentionally addressed to a person\n * rather than an agent: it tags a person (`@Person`), replies (`ParentID`) to another person's\n * message, or was judged human-addressed by the routing decision.\n */\nexport function IsAddressedToHuman(candidates: AgentTurnCandidates): boolean {\n return (candidates.MentionedUserIds?.length ?? 0) > 0\n || Boolean(candidates.ReplyParentIsHuman)\n || Boolean(candidates.DecisionAddressedToHuman);\n}\n\n/**\n * Picks the agent and route for one message, or null when no turn starts.\n *\n * The order is MJ's: the first tagged agent the host allows, a direct thread reply (`ParentID`) to\n * a runnable agent, a continuity candidate that a routing decision chose (`DecisionRouted`), and\n * then (under `Always` only) the last agent that answered, the conversation's pinned agent, the\n * host's default agent, and the conversation manager. A message that tags a person, replies to\n * another person's message, or receives a `NoAgent` routing decision with no allowed `@Agent` tag\n * starts no turn. A candidate the host doesn't allow is skipped.\n *\n * A continuity or default candidate that `isKnownAgent` doesn't recognise falls back to the\n * conversation manager, as MJ always has, rather than to the next route. A tagged agent is taken\n * as tagged; a `ThreadReply` target must pass `isRunnableAgent`.\n *\n * @param candidates The agent each route would use.\n * @param rules The host's reply mode and allowed list.\n * @param isKnownAgent True when the agent exists in the client's agent catalog.\n * @param isRunnableAgent True when the current user can run the agent directly (defaults to `isKnownAgent`).\n */\nexport function ResolveAgentTurn(\n candidates: AgentTurnCandidates,\n rules: AgentTurnRules,\n isKnownAgent: (agentId: string) => boolean,\n isRunnableAgent: (agentId: string) => boolean = isKnownAgent\n): AgentTurnTarget | null {\n const mentioned = candidates.MentionedAgentIds.find(id => IsAgentAllowed(id, rules.AllowedAgentIDs));\n if (mentioned) {\n return { AgentId: mentioned, Route: 'Mention' };\n }\n if ((candidates.MentionedUserIds?.length ?? 0) > 0 || candidates.ReplyParentIsHuman) {\n return null;\n }\n const replyAgent = candidates.ReplyParentAgentId;\n if (replyAgent && IsAgentAllowed(replyAgent, rules.AllowedAgentIDs) && isRunnableAgent(replyAgent)) {\n return { AgentId: replyAgent, Route: 'ThreadReply' };\n }\n if (candidates.DecisionAddressedToHuman) {\n return null;\n }\n if (\n candidates.ContinuityDecisionRouted\n && candidates.ContinuityAgentId\n && IsAgentAllowed(candidates.ContinuityAgentId, rules.AllowedAgentIDs)\n && isKnownAgent(candidates.ContinuityAgentId)\n ) {\n return { AgentId: candidates.ContinuityAgentId, Route: 'DecisionRouted' };\n }\n if (rules.ReplyMode === 'MentionOnly') {\n return null;\n }\n\n const implicitRoutes: ReadonlyArray<[AgentTurnRoute, string | null]> = [\n [candidates.ContinuityDecisionRouted ? 'DecisionRouted' : 'Continuity', candidates.ContinuityAgentId],\n ['ConversationDefault', candidates.ConversationDefaultAgentId],\n ['HostDefault', candidates.HostDefaultAgentId],\n ];\n for (const [route, agentId] of implicitRoutes) {\n if (agentId && IsAgentAllowed(agentId, rules.AllowedAgentIDs)) {\n return isKnownAgent(agentId)\n ? { AgentId: agentId, Route: route }\n : conversationManagerTarget(candidates, rules);\n }\n }\n return conversationManagerTarget(candidates, rules);\n}\n\n/** The conversation manager's turn, when there is one and the host allows it. */\nfunction conversationManagerTarget(candidates: AgentTurnCandidates, rules: AgentTurnRules): AgentTurnTarget | null {\n const agentId = candidates.ConversationManagerAgentId;\n return agentId && IsAgentAllowed(agentId, rules.AllowedAgentIDs)\n ? { AgentId: agentId, Route: 'ConversationManager' }\n : null;\n}\n\n/** A task-graph step that runs an agent, as the conversation manager emits it. */\ninterface AgentTaskStep {\n kind: string;\n configuration?: { agentName?: string } | object;\n}\n\n/**\n * The agent names in a task graph that the host doesn't allow. The conversation manager can\n * answer with a multi-step workflow instead of a single delegation; each `Agent` step names the\n * agent that runs it, so the allowed list has to hold there too.\n *\n * @param steps The graph's tasks.\n * @param allowedAgentIDs The host's allowed list. Null allows every agent.\n * @param resolveAgentId Maps an agent name to its ID, or null when no such agent exists.\n * @returns The disallowed names, each once, in the order they appear. Empty when all are allowed.\n */\nexport function FindDisallowedTaskGraphAgents(\n steps: ReadonlyArray<AgentTaskStep>,\n allowedAgentIDs: readonly string[] | null,\n resolveAgentId: (agentName: string) => string | null\n): string[] {\n if (allowedAgentIDs == null) {\n return [];\n }\n const disallowed: string[] = [];\n for (const step of steps) {\n const agentName = step.kind === 'Agent' ? agentNameOf(step.configuration) : null;\n if (agentName && !IsAgentAllowed(resolveAgentId(agentName), allowedAgentIDs) && !disallowed.includes(agentName)) {\n disallowed.push(agentName);\n }\n }\n return disallowed;\n}\n\n/** The `agentName` of an Agent step's configuration, when it has one. */\nfunction agentNameOf(configuration: AgentTaskStep['configuration']): string | null {\n if (configuration && 'agentName' in configuration && typeof configuration.agentName === 'string') {\n return configuration.agentName;\n }\n return null;\n}\n\n/** Minimal shape of a conversation detail row inspected when resolving continuity routing. */\nexport interface ContinuityHistoryMessage {\n ID?: string | null;\n Role?: string | null;\n AgentID?: string | null;\n ParentID?: string | null;\n Message?: string | null;\n}\n\n/**\n * Returns true when a user message explicitly `@`-mentions the given agent (by JSON mention token\n * `@{...\"id\":\"<agentId>\"...}`, JSON token `@{...\"name\":\"<agentName>\"...}`, or plain `@<agentName>`).\n */\nexport function MessageMentionsAgent(\n messageText: string | null | undefined,\n agentId: string | null | undefined,\n agentName?: string | null\n): boolean {\n if (!messageText) {\n return false;\n }\n const normalizedId = NormalizeUUID(agentId ?? '');\n const normalizedName = (agentName ?? '').trim().toLowerCase();\n\n let cursor = 0;\n while (cursor < messageText.length) {\n const openIdx = messageText.indexOf('@{', cursor);\n if (openIdx === -1) {\n break;\n }\n const closeIdx = messageText.indexOf('}', openIdx + 2);\n if (closeIdx === -1) {\n break;\n }\n const body = messageText.slice(openIdx + 2, closeIdx);\n cursor = closeIdx + 1;\n if (!body) {\n continue;\n }\n try {\n const parsed = JSON.parse(`{${body}}`) as { type?: string; id?: string; name?: string };\n if (parsed.type && parsed.type !== 'agent') {\n continue;\n }\n if (normalizedId && typeof parsed.id === 'string' && UUIDsEqual(parsed.id, normalizedId)) {\n return true;\n }\n if (normalizedName && typeof parsed.name === 'string' && parsed.name.trim().toLowerCase() === normalizedName) {\n return true;\n }\n } catch {\n // Ignore malformed token and continue scanning\n }\n }\n\n if (normalizedName) {\n const escaped = normalizedName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const plainMention = new RegExp(`(?:^|\\\\s)@${escaped}(?=\\\\b|\\\\s|$)`, 'i');\n if (plainMention.test(messageText)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Walks conversation history from newest to oldest to find the most recent non-manager `AI` message\n * that owns continuity for the next turn.\n *\n * Stops and returns `null` (returning ownership to the conversation manager) when:\n * - A `User` message explicitly `@`-mentions the conversation manager and no non-manager `AI`\n * message has replied since (e.g. the user said `@Sage …` and Sage answered directly).\n * - An `AI` message from the conversation manager was handed back from another `AI` message\n * (`ParentID` references an `AI` message, or the immediately preceding row is a non-manager `AI`\n * message in the same turn).\n */\nexport function FindContinuityAgentMessage<T extends ContinuityHistoryMessage>(\n messages: readonly T[],\n conversationManagerAgentId: string | null,\n options?: {\n conversationManagerName?: string | null;\n isWithinFloor?: (msg: T) => boolean;\n userMentionsManager?: (msg: T) => boolean;\n }\n): T | null {\n const normalizedManagerId = NormalizeUUID(conversationManagerAgentId ?? '');\n const managerName = options?.conversationManagerName ?? 'Sage';\n const byId = new Map<string, T>();\n for (const m of messages) {\n if (m.ID) {\n byId.set(NormalizeUUID(m.ID), m);\n }\n }\n\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (options?.isWithinFloor && !options.isWithinFloor(msg)) {\n continue;\n }\n if (msg.Role === 'AI' && msg.AgentID) {\n const isManager = normalizedManagerId.length > 0 && UUIDsEqual(msg.AgentID, normalizedManagerId);\n if (!isManager) {\n return msg;\n }\n // Conversation manager AI row: check if it is a handback from another AI row\n const parent = msg.ParentID ? byId.get(NormalizeUUID(msg.ParentID)) : undefined;\n if (parent && parent.Role === 'AI') {\n return null;\n }\n const prev = i > 0 ? messages[i - 1] : undefined;\n if (\n prev &&\n prev.Role === 'AI' &&\n prev.AgentID &&\n !UUIDsEqual(prev.AgentID, normalizedManagerId)\n ) {\n return null;\n }\n continue;\n }\n if (msg.Role === 'User') {\n const mentionsManager = options?.userMentionsManager\n ? options.userMentionsManager(msg)\n : MessageMentionsAgent(msg.Message, conversationManagerAgentId, managerName);\n if (mentionsManager) {\n return null;\n }\n }\n }\n return null;\n}\n\n/**\n * Convenience wrapper around {@link FindContinuityAgentMessage} returning only the `AgentID`.\n */\nexport function FindContinuityAgentId<T extends ContinuityHistoryMessage>(\n messages: readonly T[],\n conversationManagerAgentId: string | null,\n options?: {\n conversationManagerName?: string | null;\n isWithinFloor?: (msg: T) => boolean;\n userMentionsManager?: (msg: T) => boolean;\n }\n): string | null {\n return FindContinuityAgentMessage(messages, conversationManagerAgentId, options)?.AgentID ?? null;\n}\n\n"]}
|
|
@@ -32,22 +32,29 @@ export interface RoutingDecisionGate {
|
|
|
32
32
|
ReplyMode: AgentReplyMode;
|
|
33
33
|
/** Agents the message tags. */
|
|
34
34
|
MentionedAgentIds: readonly string[];
|
|
35
|
+
/** People the message tags (`@Person`). */
|
|
36
|
+
MentionedUserIds?: readonly string[];
|
|
37
|
+
/** Direct reply (`ParentID`) to an earlier AI message. */
|
|
38
|
+
ReplyParentAgentId?: string | null;
|
|
39
|
+
/** Direct reply (`ParentID`) to another person's message. */
|
|
40
|
+
ReplyParentIsHuman?: boolean;
|
|
35
41
|
/** The message, as saved. */
|
|
36
42
|
Message: string;
|
|
37
43
|
/** The last agent that answered, other than the conversation manager. */
|
|
38
44
|
ContinuityAgentId: string | null;
|
|
39
45
|
}
|
|
40
46
|
/**
|
|
41
|
-
* True when a message should get a routing decision: routing is on,
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
47
|
+
* True when a message should get a routing decision: routing is on, some agent has answered
|
|
48
|
+
* before in this conversation, and the message has no structural override (`@Agent`, `@Person`,
|
|
49
|
+
* `ParentID` thread reply, or a form response). Before any agent has answered (`ContinuityAgentId`
|
|
50
|
+
* is null), no call is made — so in `MentionOnly` chats the first invocation requires an `@Agent`
|
|
51
|
+
* mention, and subsequent unmentioned messages ask the routing decision.
|
|
45
52
|
*/
|
|
46
53
|
export declare function ShouldRunRoutingDecision(gate: RoutingDecisionGate): boolean;
|
|
47
54
|
/**
|
|
48
|
-
* Asks one routing decision and reads its answer.
|
|
49
|
-
*
|
|
50
|
-
* throws.
|
|
55
|
+
* Asks one routing decision and reads its answer. In a single-user chat it fails toward
|
|
56
|
+
* continuity; in a multi-person chat (`input.AllowNoAgentOption`) it fails toward silence
|
|
57
|
+
* (`NoAgent`). Never throws.
|
|
51
58
|
*
|
|
52
59
|
* @param input What to decide. Check it with `CanAskRoutingDecision` first.
|
|
53
60
|
* @param runDecision Runs the decision on the server.
|
|
@@ -56,8 +63,9 @@ export declare function ShouldRunRoutingDecision(gate: RoutingDecisionGate): boo
|
|
|
56
63
|
export declare function RunRoutingDecision(input: RoutingDecisionInput, runDecision: RoutingDecisionRunner, timeoutMs?: number): Promise<RoutingDecisionOutcome>;
|
|
57
64
|
/**
|
|
58
65
|
* The candidates with the decision applied: a routed agent replaces continuity and is labelled
|
|
59
|
-
* `DecisionRouted`,
|
|
60
|
-
*
|
|
66
|
+
* `DecisionRouted`, "someone else" clears continuity, and "no agent" (multi-person human-to-human)
|
|
67
|
+
* clears continuity and marks the message as addressed to a person. Any other outcome, or none,
|
|
68
|
+
* leaves the candidates as they are.
|
|
61
69
|
*/
|
|
62
70
|
export declare function ApplyRoutingDecision(candidates: AgentTurnCandidates, outcome: RoutingDecisionOutcome | null): AgentTurnCandidates;
|
|
63
71
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decision-routing.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/decision-routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"decision-routing.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/decision-routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAQH,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC9B,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAEjG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,yFAAyF;AACzF,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,iBAAiB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE9F,qEAAqE;AACrE,MAAM,WAAW,mBAAmB;IAChC,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,6BAA6B;IAC7B,SAAS,EAAE,cAAc,CAAC;IAC1B,+BAA+B;IAC/B,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,2CAA2C;IAC3C,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,0DAA0D;IAC1D,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAQ3E;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACpC,KAAK,EAAE,oBAAoB,EAC3B,WAAW,EAAE,qBAAqB,EAClC,SAAS,GAAE,MAAoC,GAChD,OAAO,CAAC,sBAAsB,CAAC,CAYjC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAChC,UAAU,EAAE,mBAAmB,EAC/B,OAAO,EAAE,sBAAsB,GAAG,IAAI,GACvC,mBAAmB,CAWrB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG7G"}
|
|
@@ -18,25 +18,28 @@
|
|
|
18
18
|
*
|
|
19
19
|
* @module @memberjunction/ng-conversations
|
|
20
20
|
*/
|
|
21
|
-
import { BuildRoutingQuestions, BuildRoutingState, ConversationUtility, DECISION_ROUTING_TIMEOUT_MS, InterpretRoutingAnswers, KeptContinuityOutcome } from '@memberjunction/ai-core-plus';
|
|
21
|
+
import { BuildRoutingQuestions, BuildRoutingState, ConversationUtility, DECISION_ROUTING_TIMEOUT_MS, InterpretRoutingAnswers, KeptContinuityOutcome, NoAgentOutcome } from '@memberjunction/ai-core-plus';
|
|
22
22
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
23
23
|
/**
|
|
24
|
-
* True when a message should get a routing decision: routing is on,
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* True when a message should get a routing decision: routing is on, some agent has answered
|
|
25
|
+
* before in this conversation, and the message has no structural override (`@Agent`, `@Person`,
|
|
26
|
+
* `ParentID` thread reply, or a form response). Before any agent has answered (`ContinuityAgentId`
|
|
27
|
+
* is null), no call is made — so in `MentionOnly` chats the first invocation requires an `@Agent`
|
|
28
|
+
* mention, and subsequent unmentioned messages ask the routing decision.
|
|
28
29
|
*/
|
|
29
30
|
export function ShouldRunRoutingDecision(gate) {
|
|
30
31
|
return gate.Enabled
|
|
31
|
-
&& gate.ReplyMode === 'Always'
|
|
32
32
|
&& !!gate.ContinuityAgentId
|
|
33
33
|
&& gate.MentionedAgentIds.length === 0
|
|
34
|
+
&& (gate.MentionedUserIds?.length ?? 0) === 0
|
|
35
|
+
&& !gate.ReplyParentAgentId
|
|
36
|
+
&& !gate.ReplyParentIsHuman
|
|
34
37
|
&& !ConversationUtility.ContainsFormResponse(gate.Message);
|
|
35
38
|
}
|
|
36
39
|
/**
|
|
37
|
-
* Asks one routing decision and reads its answer.
|
|
38
|
-
*
|
|
39
|
-
* throws.
|
|
40
|
+
* Asks one routing decision and reads its answer. In a single-user chat it fails toward
|
|
41
|
+
* continuity; in a multi-person chat (`input.AllowNoAgentOption`) it fails toward silence
|
|
42
|
+
* (`NoAgent`). Never throws.
|
|
40
43
|
*
|
|
41
44
|
* @param input What to decide. Check it with `CanAskRoutingDecision` first.
|
|
42
45
|
* @param runDecision Runs the decision on the server.
|
|
@@ -49,19 +52,25 @@ export async function RunRoutingDecision(input, runDecision, timeoutMs = DECISIO
|
|
|
49
52
|
TimeoutMS: timeoutMs
|
|
50
53
|
};
|
|
51
54
|
const result = await runWithDeadline(() => runDecision(params), timeoutMs);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
if (result) {
|
|
56
|
+
return InterpretRoutingAnswers(input, result);
|
|
57
|
+
}
|
|
58
|
+
const reason = `no answer within ${timeoutMs} ms`;
|
|
59
|
+
return input.AllowNoAgentOption ? NoAgentOutcome(reason) : KeptContinuityOutcome(reason);
|
|
55
60
|
}
|
|
56
61
|
/**
|
|
57
62
|
* The candidates with the decision applied: a routed agent replaces continuity and is labelled
|
|
58
|
-
* `DecisionRouted`,
|
|
59
|
-
*
|
|
63
|
+
* `DecisionRouted`, "someone else" clears continuity, and "no agent" (multi-person human-to-human)
|
|
64
|
+
* clears continuity and marks the message as addressed to a person. Any other outcome, or none,
|
|
65
|
+
* leaves the candidates as they are.
|
|
60
66
|
*/
|
|
61
67
|
export function ApplyRoutingDecision(candidates, outcome) {
|
|
62
68
|
if (outcome?.Verdict === 'Routed' && outcome.RoutedAgentId) {
|
|
63
69
|
return { ...candidates, ContinuityAgentId: outcome.RoutedAgentId, ContinuityDecisionRouted: true };
|
|
64
70
|
}
|
|
71
|
+
if (outcome?.Verdict === 'NoAgent') {
|
|
72
|
+
return { ...candidates, ContinuityAgentId: null, ContinuityDecisionRouted: false, DecisionAddressedToHuman: true };
|
|
73
|
+
}
|
|
65
74
|
if (outcome?.Verdict === 'SomeoneElse') {
|
|
66
75
|
return { ...candidates, ContinuityAgentId: null };
|
|
67
76
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decision-routing.js","sourceRoot":"","sources":["../../../src/lib/utils/decision-routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EACH,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"decision-routing.js","sourceRoot":"","sources":["../../../src/lib/utils/decision-routing.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Decision routing for an unmentioned message (plan Tasks 3.9 and 3.2): one typed\n * decision that may send the message to a different agent in the conversation than the last one\n * that answered, and may name the artifact version the message modifies.\n *\n * It runs before {@link ResolveAgentTurn}, which stays pure. When the answer is confident, the\n * caller replaces the continuity candidate with {@link ApplyRoutingDecision}; otherwise nothing\n * changes. Every failure keeps today's routing. The chat turns it on with `EnableDecisionRouting`,\n * which is off by default.\n *\n * The decision itself (its input, questions and state, and how its answers are read) is built in\n * `@memberjunction/ai-core-plus` (`conversation-routing-decision.ts`), so the Decision Eval harness\n * measures exactly what the chat sends. What stays here is the call and what the chat does with\n * the outcome. Import the builders and their types from `@memberjunction/ai-core-plus` directly.\n *\n * Kept free of Angular so the whole path, with the decision call mocked, is testable directly;\n * `MessageInputComponent` supplies the conversation and the call.\n *\n * @module @memberjunction/ng-conversations\n */\n\nimport {\n BuildRoutingQuestions,\n BuildRoutingState,\n ConversationUtility,\n DECISION_ROUTING_TIMEOUT_MS,\n InterpretRoutingAnswers,\n KeptContinuityOutcome,\n NoAgentOutcome,\n type RoutingDecisionInput,\n type RoutingDecisionOutcome\n} from '@memberjunction/ai-core-plus';\nimport type { RunDecisionParams, RunDecisionResult } from '@memberjunction/graphql-dataprovider';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport type { AgentReplyMode } from '../models/agent-turn.model';\nimport type { AgentTurnCandidates } from './agent-turn-routing';\n\n/** Runs a decision on the server: `ConversationAgentService.RunDecision` in the chat. */\nexport type RoutingDecisionRunner = (params: RunDecisionParams) => Promise<RunDecisionResult>;\n\n/** What decides whether a message gets a routing decision at all. */\nexport interface RoutingDecisionGate {\n /** The chat's `EnableDecisionRouting` input. */\n Enabled: boolean;\n /** The chat's reply mode. */\n ReplyMode: AgentReplyMode;\n /** Agents the message tags. */\n MentionedAgentIds: readonly string[];\n /** People the message tags (`@Person`). */\n MentionedUserIds?: readonly string[];\n /** Direct reply (`ParentID`) to an earlier AI message. */\n ReplyParentAgentId?: string | null;\n /** Direct reply (`ParentID`) to another person's message. */\n ReplyParentIsHuman?: boolean;\n /** The message, as saved. */\n Message: string;\n /** The last agent that answered, other than the conversation manager. */\n ContinuityAgentId: string | null;\n}\n\n/**\n * True when a message should get a routing decision: routing is on, some agent has answered\n * before in this conversation, and the message has no structural override (`@Agent`, `@Person`,\n * `ParentID` thread reply, or a form response). Before any agent has answered (`ContinuityAgentId`\n * is null), no call is made — so in `MentionOnly` chats the first invocation requires an `@Agent`\n * mention, and subsequent unmentioned messages ask the routing decision.\n */\nexport function ShouldRunRoutingDecision(gate: RoutingDecisionGate): boolean {\n return gate.Enabled\n && !!gate.ContinuityAgentId\n && gate.MentionedAgentIds.length === 0\n && (gate.MentionedUserIds?.length ?? 0) === 0\n && !gate.ReplyParentAgentId\n && !gate.ReplyParentIsHuman\n && !ConversationUtility.ContainsFormResponse(gate.Message);\n}\n\n/**\n * Asks one routing decision and reads its answer. In a single-user chat it fails toward\n * continuity; in a multi-person chat (`input.AllowNoAgentOption`) it fails toward silence\n * (`NoAgent`). Never throws.\n *\n * @param input What to decide. Check it with `CanAskRoutingDecision` first.\n * @param runDecision Runs the decision on the server.\n * @param timeoutMs How long to wait. Defaults to `DECISION_ROUTING_TIMEOUT_MS`.\n */\nexport async function RunRoutingDecision(\n input: RoutingDecisionInput,\n runDecision: RoutingDecisionRunner,\n timeoutMs: number = DECISION_ROUTING_TIMEOUT_MS\n): Promise<RoutingDecisionOutcome> {\n const params: RunDecisionParams = {\n State: BuildRoutingState(input),\n Questions: BuildRoutingQuestions(input),\n TimeoutMS: timeoutMs\n };\n const result = await runWithDeadline(() => runDecision(params), timeoutMs);\n if (result) {\n return InterpretRoutingAnswers(input, result);\n }\n const reason = `no answer within ${timeoutMs} ms`;\n return input.AllowNoAgentOption ? NoAgentOutcome(reason) : KeptContinuityOutcome(reason);\n}\n\n/**\n * The candidates with the decision applied: a routed agent replaces continuity and is labelled\n * `DecisionRouted`, \"someone else\" clears continuity, and \"no agent\" (multi-person human-to-human)\n * clears continuity and marks the message as addressed to a person. Any other outcome, or none,\n * leaves the candidates as they are.\n */\nexport function ApplyRoutingDecision(\n candidates: AgentTurnCandidates,\n outcome: RoutingDecisionOutcome | null\n): AgentTurnCandidates {\n if (outcome?.Verdict === 'Routed' && outcome.RoutedAgentId) {\n return { ...candidates, ContinuityAgentId: outcome.RoutedAgentId, ContinuityDecisionRouted: true };\n }\n if (outcome?.Verdict === 'NoAgent') {\n return { ...candidates, ContinuityAgentId: null, ContinuityDecisionRouted: false, DecisionAddressedToHuman: true };\n }\n if (outcome?.Verdict === 'SomeoneElse') {\n return { ...candidates, ContinuityAgentId: null };\n }\n return candidates;\n}\n\n/**\n * The artifact version to hand a turn's agent: the decision's target, when it is one of that\n * agent's versions. Null otherwise, including after a redirect to another agent.\n */\nexport function ArtifactVersionForTurn(outcome: RoutingDecisionOutcome | null, agentId: string): string | null {\n const target = outcome?.TargetArtifact;\n return target && UUIDsEqual(target.AgentId, agentId) ? target.ArtifactVersionId : null;\n}\n\n/**\n * Runs the call, or gives up on it after `timeoutMs`. Resolves with the result, a failed result\n * when the call throws, or null when it didn't answer in time. A late answer is ignored.\n */\nasync function runWithDeadline(\n call: () => Promise<RunDecisionResult>,\n timeoutMs: number\n): Promise<RunDecisionResult | null> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const deadline = new Promise<null>(resolve => {\n timer = setTimeout(() => resolve(null), timeoutMs);\n });\n const answer = Promise.resolve()\n .then(call)\n .catch((error: unknown): RunDecisionResult => ({\n Success: false,\n ErrorMessage: error instanceof Error ? error.message : String(error),\n Answers: {}\n }));\n try {\n return await Promise.race([answer, deadline]);\n } finally {\n clearTimeout(timer);\n }\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MJConversationDetailEntity } from '@memberjunction/core-entities';
|
|
2
|
+
/** Oldest first. A message with no creation time yet (an unsaved placeholder) is the newest, not the oldest. */
|
|
3
|
+
export declare function CompareMessagesByCreatedAt(a: MJConversationDetailEntity, b: MJConversationDetailEntity): number;
|
|
4
|
+
/**
|
|
5
|
+
* The list with `message` in place of the first message that has its ID, and every other copy of that ID dropped (a
|
|
6
|
+
* placeholder that just took the ID of a server row the list already holds), or with `message` appended when none has
|
|
7
|
+
* it. Returns whether it was already there.
|
|
8
|
+
*/
|
|
9
|
+
export declare function UpsertMessageById(messages: MJConversationDetailEntity[], message: MJConversationDetailEntity): {
|
|
10
|
+
Messages: MJConversationDetailEntity[];
|
|
11
|
+
Existed: boolean;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=message-list-order.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-list-order.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/message-list-order.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAEhF,gHAAgH;AAChH,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,0BAA0B,EAAE,CAAC,EAAE,0BAA0B,GAAG,MAAM,CAE/G;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,0BAA0B,EAAE,EACtC,OAAO,EAAE,0BAA0B,GAClC;IAAE,QAAQ,EAAE,0BAA0B,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAS9D"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeping the chat's message list in order while agent replies start as unsaved placeholders.
|
|
3
|
+
*
|
|
4
|
+
* Only the server writes an agent's reply (A19): the chat shows an unsaved placeholder until the server acknowledges
|
|
5
|
+
* the run, then gives the placeholder the server row's ID. Until then it has no creation time, and a refresh of the
|
|
6
|
+
* conversation can already have brought the server's copy of the same row into the list.
|
|
7
|
+
*/
|
|
8
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
9
|
+
/** Oldest first. A message with no creation time yet (an unsaved placeholder) is the newest, not the oldest. */
|
|
10
|
+
export function CompareMessagesByCreatedAt(a, b) {
|
|
11
|
+
return createdAtOf(a) - createdAtOf(b);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The list with `message` in place of the first message that has its ID, and every other copy of that ID dropped (a
|
|
15
|
+
* placeholder that just took the ID of a server row the list already holds), or with `message` appended when none has
|
|
16
|
+
* it. Returns whether it was already there.
|
|
17
|
+
*/
|
|
18
|
+
export function UpsertMessageById(messages, message) {
|
|
19
|
+
const index = messages.findIndex((m) => UUIDsEqual(m.ID, message.ID));
|
|
20
|
+
if (index < 0) {
|
|
21
|
+
return { Messages: [...messages, message], Existed: false };
|
|
22
|
+
}
|
|
23
|
+
const updated = messages
|
|
24
|
+
.map((m, i) => (i === index ? message : m))
|
|
25
|
+
.filter((m, i) => i === index || !UUIDsEqual(m.ID, message.ID));
|
|
26
|
+
return { Messages: updated, Existed: true };
|
|
27
|
+
}
|
|
28
|
+
function createdAtOf(message) {
|
|
29
|
+
return message.__mj_CreatedAt?.getTime() ?? Number.MAX_SAFE_INTEGER;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=message-list-order.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-list-order.js","sourceRoot":"","sources":["../../../src/lib/utils/message-list-order.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGpD,gHAAgH;AAChH,MAAM,UAAU,0BAA0B,CAAC,CAA6B,EAAE,CAA6B;IACrG,OAAO,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAsC,EACtC,OAAmC;IAEnC,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IACtE,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,QAAQ,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9D,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ;SACrB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAC1C,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAClE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED,SAAS,WAAW,CAAC,OAAmC;IACtD,OAAO,OAAO,CAAC,cAAc,EAAE,OAAO,EAAE,IAAI,MAAM,CAAC,gBAAgB,CAAC;AACtE,CAAC","sourcesContent":["/**\n * Keeping the chat's message list in order while agent replies start as unsaved placeholders.\n *\n * Only the server writes an agent's reply (A19): the chat shows an unsaved placeholder until the server acknowledges\n * the run, then gives the placeholder the server row's ID. Until then it has no creation time, and a refresh of the\n * conversation can already have brought the server's copy of the same row into the list.\n */\nimport { UUIDsEqual } from '@memberjunction/global';\nimport type { MJConversationDetailEntity } from '@memberjunction/core-entities';\n\n/** Oldest first. A message with no creation time yet (an unsaved placeholder) is the newest, not the oldest. */\nexport function CompareMessagesByCreatedAt(a: MJConversationDetailEntity, b: MJConversationDetailEntity): number {\n return createdAtOf(a) - createdAtOf(b);\n}\n\n/**\n * The list with `message` in place of the first message that has its ID, and every other copy of that ID dropped (a\n * placeholder that just took the ID of a server row the list already holds), or with `message` appended when none has\n * it. Returns whether it was already there.\n */\nexport function UpsertMessageById(\n messages: MJConversationDetailEntity[],\n message: MJConversationDetailEntity\n): { Messages: MJConversationDetailEntity[]; Existed: boolean } {\n const index = messages.findIndex((m) => UUIDsEqual(m.ID, message.ID));\n if (index < 0) {\n return { Messages: [...messages, message], Existed: false };\n }\n const updated = messages\n .map((m, i) => (i === index ? message : m))\n .filter((m, i) => i === index || !UUIDsEqual(m.ID, message.ID));\n return { Messages: updated, Existed: true };\n}\n\nfunction createdAtOf(message: MJConversationDetailEntity): number {\n return message.__mj_CreatedAt?.getTime() ?? Number.MAX_SAFE_INTEGER;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pinning a message on a fresh copy of its row.
|
|
3
|
+
*
|
|
4
|
+
* The chat's copy of an agent reply can carry streamed or local text that differs from the stored reply, and the
|
|
5
|
+
* server refuses a person's save that changes a reply's content (A19), so a pin saved through that copy is refused.
|
|
6
|
+
* Pinning loads the row as stored and changes only `IsPinned`, as the rating component does for its fields.
|
|
7
|
+
*/
|
|
8
|
+
import type { IMetadataProvider } from '@memberjunction/core';
|
|
9
|
+
/** Saves `IsPinned` on the stored row; false when it can't be loaded or saved. */
|
|
10
|
+
export declare function SavePinState(provider: IMetadataProvider, conversationDetailId: string, pinned: boolean): Promise<boolean>;
|
|
11
|
+
//# sourceMappingURL=pin-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pin-state.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/pin-state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAG9D,kFAAkF;AAClF,wBAAsB,YAAY,CAAC,QAAQ,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAK/H"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Saves `IsPinned` on the stored row; false when it can't be loaded or saved. */
|
|
2
|
+
export async function SavePinState(provider, conversationDetailId, pinned) {
|
|
3
|
+
const fresh = await provider.GetEntityObject('MJ: Conversation Details');
|
|
4
|
+
if (!(await fresh.Load(conversationDetailId)))
|
|
5
|
+
return false;
|
|
6
|
+
fresh.IsPinned = pinned;
|
|
7
|
+
return fresh.Save();
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=pin-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pin-state.js","sourceRoot":"","sources":["../../../src/lib/utils/pin-state.ts"],"names":[],"mappings":"AAUA,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAA2B,EAAE,oBAA4B,EAAE,MAAe;IAC3G,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,eAAe,CAA6B,0BAA0B,CAAC,CAAC;IACrG,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5D,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC;IACxB,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC","sourcesContent":["/**\n * Pinning a message on a fresh copy of its row.\n *\n * The chat's copy of an agent reply can carry streamed or local text that differs from the stored reply, and the\n * server refuses a person's save that changes a reply's content (A19), so a pin saved through that copy is refused.\n * Pinning loads the row as stored and changes only `IsPinned`, as the rating component does for its fields.\n */\nimport type { IMetadataProvider } from '@memberjunction/core';\nimport type { MJConversationDetailEntity } from '@memberjunction/core-entities';\n\n/** Saves `IsPinned` on the stored row; false when it can't be loaded or saved. */\nexport async function SavePinState(provider: IMetadataProvider, conversationDetailId: string, pinned: boolean): Promise<boolean> {\n const fresh = await provider.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details');\n if (!(await fresh.Load(conversationDetailId))) return false;\n fresh.IsPinned = pinned;\n return fresh.Save();\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { MJConversationDetailEntity } from '@memberjunction/core-entities';
|
|
2
|
+
import { UUIDMap } from '@memberjunction/global';
|
|
3
|
+
/**
|
|
4
|
+
* Minimal shape of a conversation message needed to determine whether it is a
|
|
5
|
+
* thread reply and which top-level message is its thread root.
|
|
6
|
+
*/
|
|
7
|
+
export interface ThreadReplyRow {
|
|
8
|
+
readonly ID: MJConversationDetailEntity['ID'];
|
|
9
|
+
readonly ParentID: MJConversationDetailEntity['ParentID'];
|
|
10
|
+
readonly Role: MJConversationDetailEntity['Role'] | null;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Payload emitted when the chat area scrolls to the top-level root of a
|
|
14
|
+
* collapsed thread reply so the host can open the thread and reveal the reply.
|
|
15
|
+
*/
|
|
16
|
+
export interface ThreadReplyRevealRequest {
|
|
17
|
+
readonly ReplyID: string;
|
|
18
|
+
readonly RootID: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Maps each thread reply's ID to its top-level root's ID. Rows that belong in
|
|
22
|
+
* the main conversation flow are omitted from the returned {@link UUIDMap}.
|
|
23
|
+
*
|
|
24
|
+
* Rules:
|
|
25
|
+
* - A person's row (`Role === 'User'`) with a `ParentID` is a thread reply,
|
|
26
|
+
* even when its parent is not in `rows` (so paging older rows in never flips
|
|
27
|
+
* a visible row to hidden).
|
|
28
|
+
* - Any row whose parent is a thread reply belongs to the same thread root.
|
|
29
|
+
* - Any non-person row (`AI` or `Error`) whose parent is a top-level row stays
|
|
30
|
+
* in the main flow (agent turns stamp `ParentID` to the prompt row).
|
|
31
|
+
* - A non-person row whose parent is not loaded stays in the main flow.
|
|
32
|
+
*/
|
|
33
|
+
export declare function GetThreadReplyRoots(rows: readonly ThreadReplyRow[]): UUIDMap<string>;
|
|
34
|
+
//# sourceMappingURL=thread-replies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-replies.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/thread-replies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAe,OAAO,EAAc,MAAM,wBAAwB,CAAC;AAE1E;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,0BAA0B,CAAC,IAAI,CAAC,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,0BAA0B,CAAC,UAAU,CAAC,CAAC;IAC1D,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CAC1D;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAsCpF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { IndexByUUID, UUIDMap, UUIDsEqual } from '@memberjunction/global';
|
|
2
|
+
/**
|
|
3
|
+
* Maps each thread reply's ID to its top-level root's ID. Rows that belong in
|
|
4
|
+
* the main conversation flow are omitted from the returned {@link UUIDMap}.
|
|
5
|
+
*
|
|
6
|
+
* Rules:
|
|
7
|
+
* - A person's row (`Role === 'User'`) with a `ParentID` is a thread reply,
|
|
8
|
+
* even when its parent is not in `rows` (so paging older rows in never flips
|
|
9
|
+
* a visible row to hidden).
|
|
10
|
+
* - Any row whose parent is a thread reply belongs to the same thread root.
|
|
11
|
+
* - Any non-person row (`AI` or `Error`) whose parent is a top-level row stays
|
|
12
|
+
* in the main flow (agent turns stamp `ParentID` to the prompt row).
|
|
13
|
+
* - A non-person row whose parent is not loaded stays in the main flow.
|
|
14
|
+
*/
|
|
15
|
+
export function GetThreadReplyRoots(rows) {
|
|
16
|
+
const byId = IndexByUUID(rows);
|
|
17
|
+
const memo = new Map();
|
|
18
|
+
const resolveRoot = (row) => {
|
|
19
|
+
if (memo.has(row)) {
|
|
20
|
+
return memo.get(row) ?? null;
|
|
21
|
+
}
|
|
22
|
+
memo.set(row, null);
|
|
23
|
+
const parentId = row.ParentID;
|
|
24
|
+
if (!parentId) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const parent = byId.Get(parentId);
|
|
28
|
+
if (!parent) {
|
|
29
|
+
// ponytail: a non-person row whose parent isn't loaded stays visible; if its parent later loads as a thread reply, it moves into that thread.
|
|
30
|
+
const rootId = row.Role === 'User' ? parentId : null;
|
|
31
|
+
memo.set(row, rootId);
|
|
32
|
+
return rootId;
|
|
33
|
+
}
|
|
34
|
+
const parentRoot = resolveRoot(parent);
|
|
35
|
+
const candidate = parentRoot ?? (row.Role === 'User' ? parent.ID : null);
|
|
36
|
+
const rootId = candidate && !UUIDsEqual(candidate, row.ID) ? candidate : null;
|
|
37
|
+
memo.set(row, rootId);
|
|
38
|
+
return rootId;
|
|
39
|
+
};
|
|
40
|
+
const roots = new UUIDMap();
|
|
41
|
+
for (const row of rows) {
|
|
42
|
+
const rootId = resolveRoot(row);
|
|
43
|
+
if (rootId) {
|
|
44
|
+
roots.Set(row.ID, rootId);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return roots;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=thread-replies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-replies.js","sourceRoot":"","sources":["../../../src/lib/utils/thread-replies.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAqB1E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAA+B;IACjE,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAiC,CAAC;IAEtD,MAAM,WAAW,GAAG,CAAC,GAAmB,EAAiB,EAAE;QACzD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;QAC/B,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAEpB,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAClC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,8IAA8I;YAC9I,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;YACrD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACtB,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,SAAS,GAAG,UAAU,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACzE,MAAM,MAAM,GAAG,SAAS,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9E,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACtB,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,IAAI,OAAO,EAAU,CAAC;IACpC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import type { MJConversationDetailEntity } from '@memberjunction/core-entities';\nimport { IndexByUUID, UUIDMap, UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Minimal shape of a conversation message needed to determine whether it is a\n * thread reply and which top-level message is its thread root.\n */\nexport interface ThreadReplyRow {\n readonly ID: MJConversationDetailEntity['ID'];\n readonly ParentID: MJConversationDetailEntity['ParentID'];\n readonly Role: MJConversationDetailEntity['Role'] | null;\n}\n\n/**\n * Payload emitted when the chat area scrolls to the top-level root of a\n * collapsed thread reply so the host can open the thread and reveal the reply.\n */\nexport interface ThreadReplyRevealRequest {\n readonly ReplyID: string;\n readonly RootID: string;\n}\n\n/**\n * Maps each thread reply's ID to its top-level root's ID. Rows that belong in\n * the main conversation flow are omitted from the returned {@link UUIDMap}.\n *\n * Rules:\n * - A person's row (`Role === 'User'`) with a `ParentID` is a thread reply,\n * even when its parent is not in `rows` (so paging older rows in never flips\n * a visible row to hidden).\n * - Any row whose parent is a thread reply belongs to the same thread root.\n * - Any non-person row (`AI` or `Error`) whose parent is a top-level row stays\n * in the main flow (agent turns stamp `ParentID` to the prompt row).\n * - A non-person row whose parent is not loaded stays in the main flow.\n */\nexport function GetThreadReplyRoots(rows: readonly ThreadReplyRow[]): UUIDMap<string> {\n const byId = IndexByUUID(rows);\n const memo = new Map<ThreadReplyRow, string | null>();\n\n const resolveRoot = (row: ThreadReplyRow): string | null => {\n if (memo.has(row)) {\n return memo.get(row) ?? null;\n }\n memo.set(row, null);\n\n const parentId = row.ParentID;\n if (!parentId) {\n return null;\n }\n\n const parent = byId.Get(parentId);\n if (!parent) {\n // ponytail: a non-person row whose parent isn't loaded stays visible; if its parent later loads as a thread reply, it moves into that thread.\n const rootId = row.Role === 'User' ? parentId : null;\n memo.set(row, rootId);\n return rootId;\n }\n\n const parentRoot = resolveRoot(parent);\n const candidate = parentRoot ?? (row.Role === 'User' ? parent.ID : null);\n const rootId = candidate && !UUIDsEqual(candidate, row.ID) ? candidate : null;\n memo.set(row, rootId);\n return rootId;\n };\n\n const roots = new UUIDMap<string>();\n for (const row of rows) {\n const rootId = resolveRoot(row);\n if (rootId) {\n roots.Set(row.ID, rootId);\n }\n }\n return roots;\n}\n"]}
|
package/dist/public-api.d.ts
CHANGED
|
@@ -5,9 +5,12 @@ export * from './lib/models/lazy-artifact-info';
|
|
|
5
5
|
export * from './lib/models/navigation-request.model';
|
|
6
6
|
export * from './lib/models/agent-turn.model';
|
|
7
7
|
export * from './lib/utils/agent-turn-routing';
|
|
8
|
+
export * from './lib/utils/agent-handoff-map';
|
|
8
9
|
export * from './lib/utils/decision-routing';
|
|
9
10
|
export * from './lib/utils/conversation-branching';
|
|
10
11
|
export * from './lib/utils/conversation-forks';
|
|
12
|
+
export * from './lib/utils/thread-replies';
|
|
13
|
+
export * from './lib/utils/agent-failure-display';
|
|
11
14
|
export * from './lib/services/data-cache.service';
|
|
12
15
|
export * from './lib/services/artifact-state.service';
|
|
13
16
|
export * from './lib/services/agent-state.service';
|
|
@@ -41,8 +44,10 @@ export * from './lib/components/sidebar/conversation-sidebar.component';
|
|
|
41
44
|
export * from './lib/components/sidebar/routines-section.component';
|
|
42
45
|
export * from './lib/components/conversation/conversation-list.component';
|
|
43
46
|
export * from './lib/components/conversation/conversation-chat-area.component';
|
|
47
|
+
export * from './lib/components/conversation/conversation-remote-browser-controller';
|
|
44
48
|
export * from './lib/components/conversation/conversation-empty-state.component';
|
|
45
49
|
export * from './lib/components/message/message-item.component';
|
|
50
|
+
export * from './lib/components/message/agent-handoff-card.component';
|
|
46
51
|
export * from './lib/components/message/message-list.component';
|
|
47
52
|
export * from './lib/components/message/message-input.component';
|
|
48
53
|
export * from './lib/components/composer/ai-composer.component';
|
|
@@ -124,6 +129,7 @@ export * from './lib/components/realtime/channels/client-context-channel';
|
|
|
124
129
|
export * from './lib/components/realtime/channels/realtime-channel-pane.component';
|
|
125
130
|
export * from './lib/components/realtime/channels/channel-onboarding-panel.component';
|
|
126
131
|
export * from './lib/components/realtime/whiteboard/whiteboard-channel';
|
|
132
|
+
export * from './lib/components/realtime/whiteboard/whiteboard-channel-config';
|
|
127
133
|
export * from './lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component';
|
|
128
134
|
export * from './lib/components/realtime/remote-browser/remote-browser-channel';
|
|
129
135
|
export * from './lib/components/realtime/remote-browser/remote-browser-surface.component';
|
package/dist/public-api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAKA,cAAc,4BAA4B,CAAC;AAG3C,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC;AAEtD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"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"}
|
package/dist/public-api.js
CHANGED
|
@@ -11,6 +11,7 @@ export * from './lib/models/navigation-request.model';
|
|
|
11
11
|
// Host rules for an agent turn: reply mode, routes, and the host turn handler contract
|
|
12
12
|
export * from './lib/models/agent-turn.model';
|
|
13
13
|
export * from './lib/utils/agent-turn-routing';
|
|
14
|
+
export * from './lib/utils/agent-handoff-map';
|
|
14
15
|
// Opt-in decision routing for an unmentioned message (EnableDecisionRouting)
|
|
15
16
|
// NOTE: the routing decision's builders and types, and IsAgentAllowed, live in
|
|
16
17
|
// @memberjunction/ai-core-plus — import them from there directly.
|
|
@@ -18,6 +19,8 @@ export * from './lib/utils/decision-routing';
|
|
|
18
19
|
// Fork rules: the delete guard and the open view (Main or one fork)
|
|
19
20
|
export * from './lib/utils/conversation-branching';
|
|
20
21
|
export * from './lib/utils/conversation-forks';
|
|
22
|
+
export * from './lib/utils/thread-replies';
|
|
23
|
+
export * from './lib/utils/agent-failure-display';
|
|
21
24
|
// Services - State
|
|
22
25
|
export * from './lib/services/data-cache.service';
|
|
23
26
|
export * from './lib/services/artifact-state.service';
|
|
@@ -60,8 +63,10 @@ export * from './lib/components/sidebar/conversation-sidebar.component';
|
|
|
60
63
|
export * from './lib/components/sidebar/routines-section.component';
|
|
61
64
|
export * from './lib/components/conversation/conversation-list.component';
|
|
62
65
|
export * from './lib/components/conversation/conversation-chat-area.component';
|
|
66
|
+
export * from './lib/components/conversation/conversation-remote-browser-controller';
|
|
63
67
|
export * from './lib/components/conversation/conversation-empty-state.component';
|
|
64
68
|
export * from './lib/components/message/message-item.component';
|
|
69
|
+
export * from './lib/components/message/agent-handoff-card.component';
|
|
65
70
|
export * from './lib/components/message/message-list.component';
|
|
66
71
|
export * from './lib/components/message/message-input.component';
|
|
67
72
|
// AI-aware composer wrapper (mj-ai-composer): mj-message-input-box + the three AI trigger
|
|
@@ -162,6 +167,7 @@ export * from './lib/components/realtime/channels/channel-onboarding-panel.compo
|
|
|
162
167
|
// NOTE: the whiteboard itself (engine, tools, components, export builders) lives in
|
|
163
168
|
// @memberjunction/ng-whiteboard — import board types/components from there directly.
|
|
164
169
|
export * from './lib/components/realtime/whiteboard/whiteboard-channel';
|
|
170
|
+
export * from './lib/components/realtime/whiteboard/whiteboard-channel-config';
|
|
165
171
|
export * from './lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component';
|
|
166
172
|
// Remote Browser channel plugin (client-direct, server-hosted browser) + its live surface + tools.
|
|
167
173
|
export * from './lib/components/realtime/remote-browser/remote-browser-channel';
|