crewly 1.20.364 → 1.20.366
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/config/roles/team-leader/prompt.md +2 -0
- package/config/skills/agent/core/attach-file/SKILL.md +7 -0
- package/config/skills/agent/core/attach-file/execute.sh +8 -3
- package/config/skills/agent/core/complete-task/SKILL.md +4 -2
- package/config/skills/agent/core/complete-task/execute.sh +9 -1
- package/config/skills/agent/core/complete-task/explicit-id.test.sh +13 -2
- package/config/skills/agent/core/create-task/SKILL.md +2 -0
- package/config/skills/agent/core/create-task/execute.sh +27 -1
- package/config/skills/agent/core/create-task/execute.test.sh +67 -0
- package/config/skills/agent/core/handoff-task/execute.sh +12 -5
- package/config/skills/agent/core/handoff-task/explicit-id.test.sh +70 -0
- package/config/skills/agent/core/reply/SKILL.md +10 -2
- package/config/skills/agent/core/reply-channel/SKILL.md +17 -0
- package/config/skills/agent/core/reply-channel/execute.sh +17 -2
- package/config/skills/agent/core/reply-channel/execute.test.sh +15 -2
- package/config/skills/agent/core/send-message/SKILL.md +14 -0
- package/config/skills/agent/core/slack-post/SKILL.md +3 -3
- package/config/skills/orchestrator/delegate-task/SKILL.md +1 -1
- package/config/skills/orchestrator/reply-slack/execute.sh +6 -2
- package/config/skills/orchestrator/send-pdf-to-slack/SKILL.md +2 -1
- package/config/skills/orchestrator/send-pdf-to-slack/execute.sh +10 -2
- package/config/skills/registry.json +6 -6
- package/config/skills/team-leader/delegate-task/SKILL.md +1 -1
- package/config/skills/team-leader/delegate-task/execute.sh +6 -0
- package/dist/backend/backend/src/constants.d.ts +26 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +26 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js +7 -3
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat/agent-reply.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js +2 -0
- package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts +12 -0
- package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.js +56 -7
- package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts +9 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js +159 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +89 -2
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.js +58 -4
- package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +36 -2
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +9 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +16 -10
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +19 -11
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/agent-message-envelope.d.ts +106 -0
- package/dist/backend/backend/src/services/messaging/agent-message-envelope.d.ts.map +1 -0
- package/dist/backend/backend/src/services/messaging/agent-message-envelope.js +124 -0
- package/dist/backend/backend/src/services/messaging/agent-message-envelope.js.map +1 -0
- package/dist/backend/backend/src/services/messaging/owner-hook-message.d.ts +62 -13
- package/dist/backend/backend/src/services/messaging/owner-hook-message.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/owner-hook-message.js +196 -22
- package/dist/backend/backend/src/services/messaging/owner-hook-message.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.d.ts +15 -5
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.js +22 -0
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.js.map +1 -1
- package/dist/backend/backend/src/services/orc/agent-mention-post-guard.d.ts +69 -0
- package/dist/backend/backend/src/services/orc/agent-mention-post-guard.d.ts.map +1 -0
- package/dist/backend/backend/src/services/orc/agent-mention-post-guard.js +81 -0
- package/dist/backend/backend/src/services/orc/agent-mention-post-guard.js.map +1 -0
- package/dist/backend/backend/src/services/orc/message-route.d.ts +129 -0
- package/dist/backend/backend/src/services/orc/message-route.d.ts.map +1 -0
- package/dist/backend/backend/src/services/orc/message-route.js +197 -0
- package/dist/backend/backend/src/services/orc/message-route.js.map +1 -0
- package/dist/backend/backend/src/services/orc/owner-completion-report.service.d.ts +34 -3
- package/dist/backend/backend/src/services/orc/owner-completion-report.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/owner-completion-report.service.js +65 -8
- package/dist/backend/backend/src/services/orc/owner-completion-report.service.js.map +1 -1
- package/dist/backend/backend/src/services/orc/owner-completion-report.text.d.ts +15 -2
- package/dist/backend/backend/src/services/orc/owner-completion-report.text.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/owner-completion-report.text.js +21 -6
- package/dist/backend/backend/src/services/orc/owner-completion-report.text.js.map +1 -1
- package/dist/backend/backend/src/services/orc/owner-completion-report.wiring.d.ts +16 -1
- package/dist/backend/backend/src/services/orc/owner-completion-report.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/owner-completion-report.wiring.js +19 -3
- package/dist/backend/backend/src/services/orc/owner-completion-report.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/orc/reply-destination-resolver.d.ts +76 -4
- package/dist/backend/backend/src/services/orc/reply-destination-resolver.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/reply-destination-resolver.js +343 -45
- package/dist/backend/backend/src/services/orc/reply-destination-resolver.js.map +1 -1
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.d.ts +70 -8
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.js +256 -100
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/orc/triggered-by.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/triggered-by.js +9 -0
- package/dist/backend/backend/src/services/orc/triggered-by.js.map +1 -1
- package/dist/backend/backend/src/services/orc/work-item-destination.d.ts +2 -1
- package/dist/backend/backend/src/services/orc/work-item-destination.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/work-item-destination.js +1 -1
- package/dist/backend/backend/src/services/orc/work-item-destination.js.map +1 -1
- package/dist/backend/backend/src/services/orc/work-item-destination.wiring.d.ts +16 -1
- package/dist/backend/backend/src/services/orc/work-item-destination.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/work-item-destination.wiring.js +33 -4
- package/dist/backend/backend/src/services/orc/work-item-destination.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts +1 -0
- package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/room-responder.d.ts +7 -3
- package/dist/backend/backend/src/services/slack/room-responder.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/room-responder.js +26 -20
- package/dist/backend/backend/src/services/slack/room-responder.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-agent-post.service.d.ts +1 -1
- package/dist/backend/backend/src/services/slack/slack-agent-post.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-agent-post.service.js +20 -13
- package/dist/backend/backend/src/services/slack/slack-agent-post.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +17 -0
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +35 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.d.ts +20 -1
- package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.js +58 -3
- package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +31 -0
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js +62 -5
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/trace/turn-trigger.d.ts +34 -1
- package/dist/backend/backend/src/services/trace/turn-trigger.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/turn-trigger.js +76 -6
- package/dist/backend/backend/src/services/trace/turn-trigger.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +26 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +26 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +16 -10
- package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +19 -11
- package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
- package/dist/cli/backend/src/services/messaging/agent-message-envelope.d.ts +106 -0
- package/dist/cli/backend/src/services/messaging/agent-message-envelope.d.ts.map +1 -0
- package/dist/cli/backend/src/services/messaging/agent-message-envelope.js +124 -0
- package/dist/cli/backend/src/services/messaging/agent-message-envelope.js.map +1 -0
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.d.ts +15 -5
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.js +22 -0
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.js.map +1 -1
- package/dist/cli/backend/src/services/orc/message-route.d.ts +129 -0
- package/dist/cli/backend/src/services/orc/message-route.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orc/message-route.js +197 -0
- package/dist/cli/backend/src/services/orc/message-route.js.map +1 -0
- package/dist/cli/backend/src/services/orc/owner-completion-report.text.d.ts +15 -2
- package/dist/cli/backend/src/services/orc/owner-completion-report.text.d.ts.map +1 -1
- package/dist/cli/backend/src/services/orc/owner-completion-report.text.js +21 -6
- package/dist/cli/backend/src/services/orc/owner-completion-report.text.js.map +1 -1
- package/dist/cli/backend/src/services/orc/work-item-destination.d.ts +2 -1
- package/dist/cli/backend/src/services/orc/work-item-destination.d.ts.map +1 -1
- package/dist/cli/backend/src/services/orc/work-item-destination.js +1 -1
- package/dist/cli/backend/src/services/orc/work-item-destination.js.map +1 -1
- package/dist/cli/backend/src/services/slack/room-responder.d.ts +7 -3
- package/dist/cli/backend/src/services/slack/room-responder.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/room-responder.js +26 -20
- package/dist/cli/backend/src/services/slack/room-responder.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +17 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +35 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack.service.d.ts +20 -1
- package/dist/cli/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack.service.js +58 -3
- package/dist/cli/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +31 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js +62 -5
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/cli/backend/src/services/trace/turn-trigger.d.ts +34 -1
- package/dist/cli/backend/src/services/trace/turn-trigger.d.ts.map +1 -1
- package/dist/cli/backend/src/services/trace/turn-trigger.js +76 -6
- package/dist/cli/backend/src/services/trace/turn-trigger.js.map +1 -1
- package/package.json +1 -1
|
@@ -12,9 +12,10 @@ import { REPLY_ROUTING_CONSTANTS, TURN_TRIGGER_CONSTANTS } from '../../constants
|
|
|
12
12
|
import { parseSlackThreadKey } from '../slack/slack-thread-key.js';
|
|
13
13
|
import { LoggerService } from '../core/logger.service.js';
|
|
14
14
|
import { AgentPromptReferenceService } from './agent-prompt-reference.service.js';
|
|
15
|
-
import { fixCommand, resolveReplyDestination, } from './reply-destination-resolver.js';
|
|
15
|
+
import { fixCommand, latestInputOf, resolveReplyDestination, } from './reply-destination-resolver.js';
|
|
16
16
|
import { defaultWorkDestinationDeps, deliverToWorkDestination } from './work-item-destination.wiring.js';
|
|
17
|
-
import {
|
|
17
|
+
import { originOfWorkItem } from './work-item-destination.js';
|
|
18
|
+
import { comparePostPlace, placeOfOwnerRoute, routeOfTrigger } from './message-route.js';
|
|
18
19
|
import { isSlackDm } from './orc-reply-route.service.js';
|
|
19
20
|
import { traceOutboundReply } from '../trace/trace-recorder.js';
|
|
20
21
|
import { stripTraceMarkers } from '../trace/trace-markers.js';
|
|
@@ -35,13 +36,12 @@ const logger = LoggerService.getInstance().createComponentLogger('ReplyDestinati
|
|
|
35
36
|
* Resolve and deliver an agent's message. Never guesses: a destination that
|
|
36
37
|
* does not take the message is reported as an error with the command to run.
|
|
37
38
|
*
|
|
38
|
-
* When the chosen place refuses the message, only
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* - the current work's Slack place → the owner's turn origin, else the DM.
|
|
39
|
+
* When the chosen place refuses the message, only the SAME place is tried
|
|
40
|
+
* by another way (each logged): the Slack thread a delegate was handed, or
|
|
41
|
+
* the Slack thread a chat thread with no row stands for. It is never moved
|
|
42
|
+
* to another place — not a new top-level post, not the owner DM, not the
|
|
43
|
+
* agent's last owner conversation (message-routing audit 2026-10-18, R2):
|
|
44
|
+
* the agent gets the error and the command that works.
|
|
45
45
|
*
|
|
46
46
|
* @param input - Agent, text, references, hints
|
|
47
47
|
* @param deps - Collaborators (default: the real ones)
|
|
@@ -71,8 +71,95 @@ export async function deliverReply(input, deps) {
|
|
|
71
71
|
recentDeliveries.set(dupKey, { at: Date.now(), delivery: result });
|
|
72
72
|
}
|
|
73
73
|
traceOutboundReply(clean, result);
|
|
74
|
+
if (result.ok)
|
|
75
|
+
await checkLandedRoute(clean, result, deps).catch(() => undefined);
|
|
74
76
|
return result;
|
|
75
77
|
}
|
|
78
|
+
/** Route check outcomes since boot (the regression check of the routing invariant). */
|
|
79
|
+
const routeCheckStats = { match: 0, mismatch: 0, unknown: 0 };
|
|
80
|
+
/**
|
|
81
|
+
* Counts of the route check since boot.
|
|
82
|
+
*
|
|
83
|
+
* @returns match / mismatch / unknown counts
|
|
84
|
+
*/
|
|
85
|
+
export function getRouteCheckStats() {
|
|
86
|
+
return { ...routeCheckStats };
|
|
87
|
+
}
|
|
88
|
+
/** Zero the route check counts (tests). */
|
|
89
|
+
export function resetRouteCheckStatsForTesting() {
|
|
90
|
+
routeCheckStats.match = 0;
|
|
91
|
+
routeCheckStats.mismatch = 0;
|
|
92
|
+
routeCheckStats.unknown = 0;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Compare where a delivered post landed with the place of the request it
|
|
96
|
+
* answers (the route of the agent's latest routed input), and log a
|
|
97
|
+
* mismatch. A post the agent addressed by an explicit reference answers that
|
|
98
|
+
* reference and is not checked; an internal message to an agent has no
|
|
99
|
+
* Slack place. Never throws, never blocks: it is a regression signal.
|
|
100
|
+
*
|
|
101
|
+
* @param input - The delivered input
|
|
102
|
+
* @param delivery - Where it landed
|
|
103
|
+
* @param deps - Collaborators (default: the real ones)
|
|
104
|
+
* @returns The outcome, or null when not checked
|
|
105
|
+
*/
|
|
106
|
+
export async function checkLandedRoute(input, delivery, deps) {
|
|
107
|
+
const dest = delivery.destination;
|
|
108
|
+
if (REFERENCE_SOURCES.has(dest.source) || dest.source === 'prompt' || delivery.agentSession)
|
|
109
|
+
return null;
|
|
110
|
+
const d = deps ?? (await defaultReplyDeliveryDeps());
|
|
111
|
+
const trigger = d.routedTrigger?.(input.session) ?? null;
|
|
112
|
+
const route = routeOfTrigger(trigger, d.resolver.turnOrigin(input.session));
|
|
113
|
+
if (!route)
|
|
114
|
+
return null;
|
|
115
|
+
const expected = await expectedPlaceOf(route, d);
|
|
116
|
+
const landed = {
|
|
117
|
+
...(delivery.conversationId ? { conversationId: delivery.conversationId } : {}),
|
|
118
|
+
...(dest.kind === 'conversation' && dest.thread ? { thread: dest.thread } : {}),
|
|
119
|
+
...(delivery.slackChannelId ? { slackChannelId: delivery.slackChannelId } : {}),
|
|
120
|
+
...(delivery.threadTs ? { threadTs: delivery.threadTs } : {}),
|
|
121
|
+
};
|
|
122
|
+
const outcome = expected === 'internal' ? 'mismatch' : comparePostPlace(expected, landed);
|
|
123
|
+
routeCheckStats[outcome] += 1;
|
|
124
|
+
if (outcome === 'mismatch') {
|
|
125
|
+
logger.warn('[RouteCheck] Post landed outside the thread of the request it answers', {
|
|
126
|
+
session: input.session,
|
|
127
|
+
route,
|
|
128
|
+
expected: expected === 'internal' ? 'internal (agent-to-agent)' : expected,
|
|
129
|
+
landed,
|
|
130
|
+
source: dest.source,
|
|
131
|
+
mismatches: routeCheckStats.mismatch,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return outcome;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The place a route's answer belongs in: an owner route's thread, a work
|
|
138
|
+
* item's owner origin, or `internal` for work an agent delegated (its answer
|
|
139
|
+
* goes to that agent, never to Slack).
|
|
140
|
+
*
|
|
141
|
+
* @param route - The route
|
|
142
|
+
* @param d - Collaborators
|
|
143
|
+
* @returns The place, `internal`, or null when unknown
|
|
144
|
+
*/
|
|
145
|
+
async function expectedPlaceOf(route, d) {
|
|
146
|
+
if (route.kind === 'owner')
|
|
147
|
+
return placeOfOwnerRoute(route);
|
|
148
|
+
const wi = await d.resolver.workItem(route.workItemId).catch(() => null);
|
|
149
|
+
const origin = wi ? originOfWorkItem(wi) : null;
|
|
150
|
+
if (!origin)
|
|
151
|
+
return null;
|
|
152
|
+
if (origin.kind === 'agent')
|
|
153
|
+
return 'internal';
|
|
154
|
+
if (origin.kind !== 'owner')
|
|
155
|
+
return null;
|
|
156
|
+
return {
|
|
157
|
+
...(origin.conversationId ? { conversationId: origin.conversationId } : {}),
|
|
158
|
+
...(origin.chatThreadId ? { thread: origin.chatThreadId } : {}),
|
|
159
|
+
...(origin.slackChannelId ? { slackChannelId: origin.slackChannelId } : {}),
|
|
160
|
+
...(origin.threadTs ? { threadTs: origin.threadTs } : {}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
76
163
|
/**
|
|
77
164
|
* {@link deliverReply} without the run-trace record.
|
|
78
165
|
*
|
|
@@ -103,16 +190,15 @@ async function deliverReplyUntraced(input, deps) {
|
|
|
103
190
|
const delegated = await postToDelegatedThread(input, dest, d);
|
|
104
191
|
if (delegated)
|
|
105
192
|
return delegated;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return viaDm;
|
|
193
|
+
// Work handed over from another agent's DM with the owner: this agent
|
|
194
|
+
// cannot post there, and its answer is not moved to its own DM — it goes
|
|
195
|
+
// to the agent the owner asked (message-routing audit 2026-10-18, R2).
|
|
196
|
+
if (!result.ok && dest.kind === 'conversation' && (await isOthersOwnerDm(input.session, dest.conversationId, d))) {
|
|
197
|
+
const delegator = await delegatorOfWork(input, d);
|
|
198
|
+
return {
|
|
199
|
+
...result,
|
|
200
|
+
error: notDelivered(`conversation ${dest.conversationId} is another agent's DM with the owner, where you cannot post`, delegator ? `send-message --to ${delegator} "<your answer>" (${delegator} answers the owner there)` : 'send your answer to the agent who handed you this work (send-message --to <agent>)'),
|
|
201
|
+
};
|
|
116
202
|
}
|
|
117
203
|
const hintedThread = dest.source === 'hint' && dest.kind === 'conversation' ? dest.thread : undefined;
|
|
118
204
|
if (dest.source === 'hint') {
|
|
@@ -128,25 +214,10 @@ async function deliverReplyUntraced(input, deps) {
|
|
|
128
214
|
logger.warn('The conversation the agent named did not take its message', { session: input.session, error: result.error });
|
|
129
215
|
return withCardFix(result, hintedThread, d);
|
|
130
216
|
}
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (fallback) {
|
|
136
|
-
logger.warn('Current work destination did not take the message — trying the next real place', {
|
|
137
|
-
session: input.session,
|
|
138
|
-
from: dest.kind === 'work' ? dest.destination.kind : 'conversation',
|
|
139
|
-
to: fallback.kind === 'work' ? fallback.destination.kind : `conversation ${fallback.conversationId}`,
|
|
140
|
-
});
|
|
141
|
-
const retry = await attempt(input, fallback, prompt, d);
|
|
142
|
-
if (retry.ok)
|
|
143
|
-
return retry;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
// An answered reference (decision / ticket / work item / message) must not
|
|
147
|
-
// die in a thread the conversation could not post into: post straight to
|
|
148
|
-
// that Slack thread, then as a new top-level post in the room's channel.
|
|
149
|
-
if (dest.kind === 'conversation' && REFERENCE_SOURCES.has(dest.source)) {
|
|
217
|
+
// An answered reference (decision / ticket / work item / message / route)
|
|
218
|
+
// must not die in a thread the conversation could not post into: post
|
|
219
|
+
// straight to that same Slack thread. Never a new top-level post instead.
|
|
220
|
+
if (dest.kind === 'conversation' && (REFERENCE_SOURCES.has(dest.source) || dest.source === 'route' || dest.source === 'current-work' || dest.source === 'turn-origin')) {
|
|
150
221
|
for (const fallback of await referenceFallbacks(input.session, dest, d)) {
|
|
151
222
|
logger.warn('Referenced conversation did not take the reply — trying the next real place', {
|
|
152
223
|
session: input.session,
|
|
@@ -260,8 +331,8 @@ async function withCardFix(result, thread, d) {
|
|
|
260
331
|
const REFERENCE_SOURCES = new Set(['decision', 'ticket', 'work-item', 'message']);
|
|
261
332
|
/**
|
|
262
333
|
* Where an answered reference goes when its conversation refused the post:
|
|
263
|
-
* the Slack thread it names (a card posted straight to Slack has no chat
|
|
264
|
-
*
|
|
334
|
+
* the Slack thread it names (a card posted straight to Slack has no chat
|
|
335
|
+
* row). Only that same thread — never a new top-level post.
|
|
265
336
|
*
|
|
266
337
|
* @param session - Agent
|
|
267
338
|
* @param failed - The conversation destination that refused
|
|
@@ -280,13 +351,7 @@ async function referenceFallbacks(session, failed, d) {
|
|
|
280
351
|
reason: `conversation ${failed.conversationId} could not post into thread ${failed.thread} — Slack thread post`,
|
|
281
352
|
});
|
|
282
353
|
}
|
|
283
|
-
|
|
284
|
-
out.push({
|
|
285
|
-
kind: 'work',
|
|
286
|
-
destination: { kind: 'new-top-level', ...(wi ? { topic: shortTopic(wi.title) } : {}), reason: `conversation ${failed.conversationId} did not take the reply` },
|
|
287
|
-
source: failed.source,
|
|
288
|
-
reason: `conversation ${failed.conversationId} did not take the reply — new top-level post`,
|
|
289
|
-
});
|
|
354
|
+
void session;
|
|
290
355
|
return out;
|
|
291
356
|
}
|
|
292
357
|
/**
|
|
@@ -298,67 +363,45 @@ async function referenceFallbacks(session, failed, d) {
|
|
|
298
363
|
* @param prompt - The agent's prompt reference
|
|
299
364
|
* @returns A different instruction, or empty when there is none
|
|
300
365
|
*/
|
|
366
|
+
/**
|
|
367
|
+
* What to do when the place a reply resolved to cannot take it. Resending
|
|
368
|
+
* the same `reply` would resolve to the same place, so the advice names
|
|
369
|
+
* the explicit alternatives (message-routing audit 2026-10-18: refusals
|
|
370
|
+
* must be actionable).
|
|
371
|
+
*/
|
|
372
|
+
const UNPOSTABLE_ADVICE = 'reply --conversation <id> --thread <thread> "<message>" to name a place you can post in, or tell whoever gave you this work (send-message --to <session> "<message>")';
|
|
301
373
|
function retryAdvice(dest, prompt) {
|
|
302
374
|
const same = REFERENCE_SOURCES.has(dest.source) || dest.source === 'prompt';
|
|
303
375
|
const cmd = fixCommand(prompt);
|
|
304
376
|
return same && cmd !== 'reply "<your message>"' ? '' : cmd;
|
|
305
377
|
}
|
|
306
378
|
/**
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
* is opened with `Re: <work title>` so the owner sees what it answers.
|
|
379
|
+
* Whether a conversation is another agent's DM with the owner (a Slack DM
|
|
380
|
+
* the replying agent is not in).
|
|
310
381
|
*
|
|
311
|
-
* @param
|
|
312
|
-
* @param
|
|
382
|
+
* @param session - Replying agent
|
|
383
|
+
* @param conversationId - The conversation that refused
|
|
313
384
|
* @param d - Collaborators
|
|
314
|
-
* @returns
|
|
385
|
+
* @returns True for another agent's owner DM
|
|
315
386
|
*/
|
|
316
|
-
async function
|
|
317
|
-
|
|
318
|
-
return null;
|
|
319
|
-
const slack = d.resolver.slackChannelOfConversation(failed.conversationId);
|
|
387
|
+
async function isOthersOwnerDm(session, conversationId, d) {
|
|
388
|
+
const slack = d.resolver.slackChannelOfConversation(conversationId);
|
|
320
389
|
if (!slack || !isSlackDm(slack))
|
|
321
|
-
return
|
|
322
|
-
|
|
323
|
-
return null;
|
|
324
|
-
const dm = await d.resolver.ownerDm(input.session).catch(() => null);
|
|
325
|
-
if (!dm || dm === failed.conversationId)
|
|
326
|
-
return null;
|
|
327
|
-
const wi = input.reference?.workItemId
|
|
328
|
-
? await d.resolver.workItem(input.reference.workItemId).catch(() => null)
|
|
329
|
-
: currentWorkItemOf(await d.resolver.poolItems().catch(() => []), input.session);
|
|
330
|
-
const topic = wi ? `Re: ${shortTopic(wi.title)}` : undefined;
|
|
331
|
-
return {
|
|
332
|
-
dest: { kind: 'conversation', conversationId: dm, source: 'owner-dm', reason: 'the owner asked in a DM with another agent — your DM with the owner' },
|
|
333
|
-
content: withTopicLine(topic, input.content),
|
|
334
|
-
};
|
|
390
|
+
return false;
|
|
391
|
+
return !(await d.resolver.ownsConversation(session, conversationId).catch(() => true));
|
|
335
392
|
}
|
|
336
393
|
/**
|
|
337
|
-
* The
|
|
394
|
+
* The agent that handed the replier the work it is answering, when known.
|
|
338
395
|
*
|
|
339
|
-
* @param
|
|
340
|
-
* @param failed - The destination that refused
|
|
396
|
+
* @param input - Agent, reference
|
|
341
397
|
* @param d - Collaborators
|
|
342
|
-
* @returns
|
|
398
|
+
* @returns The delegator's session, or null
|
|
343
399
|
*/
|
|
344
|
-
async function
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
kind: 'work',
|
|
350
|
-
destination: { kind: 'new-top-level', ...(wi ? { topic: shortTopic(wi.title) } : {}), reason: `conversation ${failed.conversationId} did not take the reply` },
|
|
351
|
-
source: 'current-work',
|
|
352
|
-
reason: `conversation ${failed.conversationId} did not take the reply — new top-level post`,
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
const origin = d.resolver.turnOrigin(session);
|
|
356
|
-
if (origin) {
|
|
357
|
-
const thread = origin.slackThreadKey ?? origin.chatThreadId;
|
|
358
|
-
return { kind: 'conversation', conversationId: origin.conversationId, ...(thread ? { thread } : {}), source: 'turn-origin', reason: 'the work destination did not take the reply — your last owner conversation' };
|
|
359
|
-
}
|
|
360
|
-
const dm = await d.resolver.ownerDm(session).catch(() => null);
|
|
361
|
-
return dm ? { kind: 'conversation', conversationId: dm, source: 'owner-dm', reason: 'the work destination did not take the reply — your DM with the owner' } : null;
|
|
400
|
+
async function delegatorOfWork(input, d) {
|
|
401
|
+
const id = input.reference?.workItemId ?? d.resolver.latestInput?.(input.session)?.workItemId;
|
|
402
|
+
const wi = id ? await d.resolver.workItem(id).catch(() => null) : null;
|
|
403
|
+
const by = wi?.metadata?.delegatedBy;
|
|
404
|
+
return typeof by === 'string' && by && by !== input.session ? by : null;
|
|
362
405
|
}
|
|
363
406
|
/**
|
|
364
407
|
* Why the agent is posting: the provenance record for the row and the text
|
|
@@ -371,15 +414,21 @@ async function currentWorkFallback(session, failed, d) {
|
|
|
371
414
|
* @param d - Collaborators
|
|
372
415
|
* @returns Metadata and the text to post, or null when no trigger is known
|
|
373
416
|
*/
|
|
374
|
-
async function provenanceOf(input, postedConversationId, d) {
|
|
417
|
+
async function provenanceOf(input, postedConversationId, d, answering) {
|
|
375
418
|
try {
|
|
376
|
-
const trigger =
|
|
419
|
+
const trigger = triggerOfAnsweredInput(input.session, d, answering);
|
|
377
420
|
if (!trigger)
|
|
378
421
|
return null;
|
|
379
422
|
const workItem = trigger.workItemId ? await d.resolver.workItem(trigger.workItemId).catch(() => null) : null;
|
|
423
|
+
// The post answers the owner's message in the conversation it lands in
|
|
424
|
+
// when the owner wrote to the agent there — even when a newer owner
|
|
425
|
+
// message elsewhere is the agent's latest (R8).
|
|
426
|
+
const answersOwnerHere = !!postedConversationId &&
|
|
427
|
+
((d.ownerAskedRecently?.(input.session, postedConversationId) ?? false) ||
|
|
428
|
+
(trigger.triggerKind === 'owner_message' && trigger.conversationId !== postedConversationId && (d.ownerAskedEarlier?.(input.session, postedConversationId) ?? false)));
|
|
380
429
|
const phrase = triggeredByPhrase(trigger, {
|
|
381
430
|
...(postedConversationId ? { postedConversationId } : {}),
|
|
382
|
-
ownerAskedHere:
|
|
431
|
+
ownerAskedHere: answersOwnerHere,
|
|
383
432
|
workItem,
|
|
384
433
|
...(d.conversationPlace ? { conversationPlace: d.conversationPlace } : {}),
|
|
385
434
|
});
|
|
@@ -394,6 +443,23 @@ async function provenanceOf(input, postedConversationId, d) {
|
|
|
394
443
|
return null;
|
|
395
444
|
}
|
|
396
445
|
}
|
|
446
|
+
/**
|
|
447
|
+
* The trigger of the input a post answers: for a post addressed to a work
|
|
448
|
+
* item, that work item's dispatch (the session's own trigger when it is that
|
|
449
|
+
* dispatch), never whatever was delivered to the agent last (R8).
|
|
450
|
+
*
|
|
451
|
+
* @param session - Agent
|
|
452
|
+
* @param d - Collaborators
|
|
453
|
+
* @param answering - The work item the post names, when it names one
|
|
454
|
+
* @returns The trigger, or null
|
|
455
|
+
*/
|
|
456
|
+
function triggerOfAnsweredInput(session, d, answering) {
|
|
457
|
+
const current = d.turnTrigger?.(session) ?? null;
|
|
458
|
+
const id = answering?.workItemId;
|
|
459
|
+
if (!id || current?.workItemId === id)
|
|
460
|
+
return current;
|
|
461
|
+
return { triggerKind: 'dispatch', workItemId: id, rootKind: 'dispatch', rootRef: id, viaAgents: [], at: Date.now(), ...(current?.traceId ? { traceId: current.traceId } : {}) };
|
|
462
|
+
}
|
|
397
463
|
/**
|
|
398
464
|
* Provenance for a post an agent makes straight into a conversation
|
|
399
465
|
* (`reply-channel`, `POST /chat/channels/:id/messages`), outside
|
|
@@ -431,7 +497,8 @@ async function attempt(input, dest, prompt, d) {
|
|
|
431
497
|
const ticket = dest.source === 'ticket' ? input.reference?.ticket : dest.source === 'prompt' ? prompt?.ticket : undefined;
|
|
432
498
|
const deliversTicket = ticket ? { [REPLY_ROUTING_CONSTANTS.DELIVERS_TICKET_METADATA_KEY]: ticket } : undefined;
|
|
433
499
|
const answeredReference = dest.source === 'prompt' || dest.source === 'ticket' || dest.source === 'decision' || dest.source === 'work-item' || dest.source === 'message';
|
|
434
|
-
const
|
|
500
|
+
const answeredWorkItem = dest.source === 'work-item' ? input.reference?.workItemId : dest.source === 'prompt' ? prompt?.workItemId : undefined;
|
|
501
|
+
const provenance = await provenanceOf(input, dest.kind === 'conversation' ? dest.conversationId : undefined, d, answeredWorkItem ? { workItemId: answeredWorkItem } : undefined);
|
|
435
502
|
const metadata = { ...(deliversTicket ?? {}), ...(provenance?.metadata ?? {}) };
|
|
436
503
|
if (provenance?.content !== undefined)
|
|
437
504
|
input = { ...input, content: provenance.content };
|
|
@@ -461,18 +528,27 @@ async function attempt(input, dest, prompt, d) {
|
|
|
461
528
|
const why = member
|
|
462
529
|
? `conversation ${dest.conversationId} (${dest.reason}) could not post it into its Slack thread`
|
|
463
530
|
: `conversation ${dest.conversationId} (${dest.reason}) did not take it — you are not a member there`;
|
|
464
|
-
|
|
531
|
+
// A work item's place is fixed by the item: resending the same reply lands there again.
|
|
532
|
+
const advice = dest.source === 'work-item' || dest.source === 'current-work' || dest.source === 'route' ? UNPOSTABLE_ADVICE : retryAdvice(dest, prompt);
|
|
533
|
+
return { ok: false, destination: dest, error: notDelivered(why, advice) };
|
|
465
534
|
}
|
|
466
535
|
if (answeredReference && input.interim !== true)
|
|
467
536
|
AgentPromptReferenceService.getInstance().clear(input.session);
|
|
468
537
|
return { ok: true, destination: dest, messageId, conversationId: dest.conversationId };
|
|
469
538
|
}
|
|
539
|
+
if (dest.source === 'agent-message' && dest.destination.kind === 'agent' && d.answerAgent) {
|
|
540
|
+
const to = dest.destination.sessionName;
|
|
541
|
+
const taken = await d.answerAgent(input.session, to, input.content).catch(() => false);
|
|
542
|
+
if (!taken)
|
|
543
|
+
return { ok: false, destination: dest, error: notDelivered(`${dest.reason}: ${to} could not be reached`, `send-message --to ${to} "<your answer>"`) };
|
|
544
|
+
return { ok: true, destination: dest, agentSession: to };
|
|
545
|
+
}
|
|
470
546
|
try {
|
|
471
547
|
const wd = await d.workDestination();
|
|
472
548
|
const posted = await deliverToWorkDestination(input.session, dest.destination, input.content, wd);
|
|
473
549
|
if (!posted) {
|
|
474
550
|
const why = dest.destination.kind === 'agent' ? `${dest.reason}: ${dest.destination.sessionName} could not be reached` : `${dest.reason}: there is no Slack channel to post in`;
|
|
475
|
-
return { ok: false, destination: dest, error: notDelivered(why,
|
|
551
|
+
return { ok: false, destination: dest, error: notDelivered(why, UNPOSTABLE_ADVICE) };
|
|
476
552
|
}
|
|
477
553
|
if (answeredReference && input.interim !== true)
|
|
478
554
|
AgentPromptReferenceService.getInstance().clear(input.session);
|
|
@@ -486,9 +562,35 @@ async function attempt(input, dest, prompt, d) {
|
|
|
486
562
|
};
|
|
487
563
|
}
|
|
488
564
|
catch (err) {
|
|
489
|
-
return { ok: false, destination: dest, error: notDelivered(`posting to Slack failed (${err instanceof Error ? err.message : String(err)})`,
|
|
565
|
+
return { ok: false, destination: dest, error: notDelivered(`posting to Slack failed (${err instanceof Error ? err.message : String(err)})`, UNPOSTABLE_ADVICE) };
|
|
490
566
|
}
|
|
491
567
|
}
|
|
568
|
+
/**
|
|
569
|
+
* Whether a chat thread is agent-to-agent coordination kept inside Crewly
|
|
570
|
+
* (crewly#1340): the message, or the root of its thread, carries
|
|
571
|
+
* `agentCoordinationInternal`. `reply-channel` keeps a post in such a thread
|
|
572
|
+
* internal; a file must not go to the Slack thread key stored on that
|
|
573
|
+
* message either (Pia's v3/v4 videos for Ella landed in Steve's thread,
|
|
574
|
+
* 2026-10-18).
|
|
575
|
+
*
|
|
576
|
+
* @param threadId - A chat message id (anything else is not internal)
|
|
577
|
+
* @param deps - Collaborators (default: the real ones)
|
|
578
|
+
* @returns Why the thread is internal, or null
|
|
579
|
+
*/
|
|
580
|
+
export async function internalCoordinationThread(threadId, deps) {
|
|
581
|
+
if (typeof threadId !== 'string' || !threadId.trim() || parseSlackThreadKey(threadId))
|
|
582
|
+
return null;
|
|
583
|
+
const d = deps ?? (await defaultReplyDeliveryDeps());
|
|
584
|
+
const { AGENT_COORDINATION_CONSTANTS } = await import('../../constants.js');
|
|
585
|
+
const reasonOf = (id) => {
|
|
586
|
+
const v = id ? d.resolver.getMessage(id)?.metadata?.[AGENT_COORDINATION_CONSTANTS.INTERNAL_METADATA_KEY] : undefined;
|
|
587
|
+
return typeof v === 'string' && v ? v : null;
|
|
588
|
+
};
|
|
589
|
+
const msg = d.resolver.getMessage(threadId.trim());
|
|
590
|
+
if (!msg)
|
|
591
|
+
return null;
|
|
592
|
+
return reasonOf(msg.id) ?? reasonOf(msg.threadId);
|
|
593
|
+
}
|
|
492
594
|
/**
|
|
493
595
|
* The Slack place (channel + thread) the resolver gives, without posting —
|
|
494
596
|
* for files and the `[DONE]` notice. Ticket threads that do not exist yet
|
|
@@ -576,6 +678,38 @@ function logResolution(session, r) {
|
|
|
576
678
|
reason: d.reason,
|
|
577
679
|
});
|
|
578
680
|
}
|
|
681
|
+
/**
|
|
682
|
+
* The real {@link ReplyDeliveryDeps.answerAgent}: the answer goes to the
|
|
683
|
+
* other agent with the replier's sender header (so it can answer back), and
|
|
684
|
+
* the replier's open ask from that agent counts as answered.
|
|
685
|
+
*
|
|
686
|
+
* @param fromSession - Replying agent
|
|
687
|
+
* @param toSession - Agent whose message it answers
|
|
688
|
+
* @param text - Answer
|
|
689
|
+
* @returns True when the message was taken
|
|
690
|
+
*/
|
|
691
|
+
async function defaultAnswerAgent(fromSession, toSession, text) {
|
|
692
|
+
const [{ getAgentMessenger }, { describeAgentSender, wrapAgentMessage }, { StorageService }, { getAgentAskLedger }] = await Promise.all([
|
|
693
|
+
import('./work-item-destination.wiring.js'),
|
|
694
|
+
import('../messaging/agent-message-envelope.js'),
|
|
695
|
+
import('../core/storage.service.js'),
|
|
696
|
+
import('../messaging/agent-ask-ledger.service.js'),
|
|
697
|
+
]);
|
|
698
|
+
const messenger = getAgentMessenger();
|
|
699
|
+
if (!messenger)
|
|
700
|
+
return false;
|
|
701
|
+
const sender = (await describeAgentSender(fromSession, (s) => StorageService.getInstance().findMemberBySessionName(s))) ?? { session: fromSession };
|
|
702
|
+
const taken = await messenger(toSession, wrapAgentMessage(text, sender));
|
|
703
|
+
if (taken) {
|
|
704
|
+
try {
|
|
705
|
+
getAgentAskLedger()?.noteAnswered(fromSession, toSession);
|
|
706
|
+
}
|
|
707
|
+
catch {
|
|
708
|
+
/* best effort */
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return taken;
|
|
712
|
+
}
|
|
579
713
|
/**
|
|
580
714
|
* The real collaborators (lazy imports keep this module light for tests).
|
|
581
715
|
*
|
|
@@ -708,6 +842,9 @@ export async function defaultReplyDeliveryDeps() {
|
|
|
708
842
|
});
|
|
709
843
|
},
|
|
710
844
|
surfacedOwnerConversations: (session) => SubAgentMessageQueue.getInstance().surfacedUnansweredConversations(session),
|
|
845
|
+
// What the agent answers: the latest input that names it, not a reminder
|
|
846
|
+
// or notice delivered since (message-routing audit 2026-10-18).
|
|
847
|
+
latestInput: (session) => latestInputOf(getTurnTriggerTracker().routedTrigger(session)),
|
|
711
848
|
now: () => Date.now(),
|
|
712
849
|
};
|
|
713
850
|
const { deliverAgentReplyToConversation } = await import('../../controllers/chat/chat.controller.js');
|
|
@@ -716,6 +853,7 @@ export async function defaultReplyDeliveryDeps() {
|
|
|
716
853
|
resolver,
|
|
717
854
|
deliverToConversation: deliverAgentReplyToConversation,
|
|
718
855
|
workDestination: defaultWorkDestinationDeps,
|
|
856
|
+
answerAgent: defaultAnswerAgent,
|
|
719
857
|
priorRoomAnswer: async (input) => {
|
|
720
858
|
const { getSlackTeamChannelService } = await import('../slack/slack-team-channel.service.js');
|
|
721
859
|
return (await getSlackTeamChannelService()?.heldReplyFor(input)) ?? null;
|
|
@@ -727,6 +865,8 @@ export async function defaultReplyDeliveryDeps() {
|
|
|
727
865
|
},
|
|
728
866
|
turnTrigger: (session) => getTurnTriggerTracker().trigger(session),
|
|
729
867
|
ownerAskedRecently: (session, conversationId) => getTurnTriggerTracker().ownerAskedRecently(session, conversationId),
|
|
868
|
+
ownerAskedEarlier: (session, conversationId) => getTurnTriggerTracker().ownerAskedWithin(session, conversationId, TURN_TRIGGER_CONSTANTS.FRESH_MS),
|
|
869
|
+
routedTrigger: (session) => getTurnTriggerTracker().routedTrigger(session),
|
|
730
870
|
currentTrace: (session) => getTraceContext().currentTrace(session),
|
|
731
871
|
conversationPlace: (id) => chatConversationPlace(chatLookup, id),
|
|
732
872
|
};
|
|
@@ -766,4 +906,20 @@ export function chatConversationPlace(chat, conversationId) {
|
|
|
766
906
|
return null;
|
|
767
907
|
}
|
|
768
908
|
}
|
|
909
|
+
/**
|
|
910
|
+
* The route an agent's own messages carry onward: what it is answering now
|
|
911
|
+
* (its latest routed input; message-route.ts). Never throws.
|
|
912
|
+
*
|
|
913
|
+
* @param session - Agent session
|
|
914
|
+
* @returns The route, or null when its input names none
|
|
915
|
+
*/
|
|
916
|
+
export async function currentRouteOf(session) {
|
|
917
|
+
try {
|
|
918
|
+
const { OrcReplyRouteService } = await import('./orc-reply-route.service.js');
|
|
919
|
+
return routeOfTrigger(getTurnTriggerTracker().routedTrigger(session), OrcReplyRouteService.getInstance().getLastOrigin(session));
|
|
920
|
+
}
|
|
921
|
+
catch {
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
769
925
|
//# sourceMappingURL=reply-destination.wiring.js.map
|