@athenaintel/react 0.55.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -1
- package/dist/agent-runtime/src/answer-label.cjs +8 -2
- package/dist/agent-runtime/src/answer-label.cjs.map +1 -1
- package/dist/agent-runtime/src/answer-label.d.ts +10 -4
- package/dist/agent-runtime/src/answer-label.js +8 -2
- package/dist/agent-runtime/src/answer-label.js.map +1 -1
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs +6 -3
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -1
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js +6 -3
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -1
- package/dist/agent-runtime/src/edit-command.cjs +40 -19
- package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
- package/dist/agent-runtime/src/edit-command.d.ts +2 -0
- package/dist/agent-runtime/src/edit-command.js +41 -20
- package/dist/agent-runtime/src/edit-command.js.map +1 -1
- package/dist/agent-runtime/src/generated/runconfig-contract.cjs +8 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
- package/dist/agent-runtime/src/generated/runconfig-contract.d.ts +9 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.js +8 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
- package/dist/agent-runtime/src/history-store.cjs +426 -0
- package/dist/agent-runtime/src/history-store.cjs.map +1 -0
- package/dist/agent-runtime/src/history-store.d.ts +282 -0
- package/dist/agent-runtime/src/history-store.js +426 -0
- package/dist/agent-runtime/src/history-store.js.map +1 -0
- package/dist/agent-runtime/src/inbound-channel.cjs +96 -0
- package/dist/agent-runtime/src/inbound-channel.cjs.map +1 -0
- package/dist/agent-runtime/src/inbound-channel.d.ts +75 -0
- package/dist/agent-runtime/src/inbound-channel.js +96 -0
- package/dist/agent-runtime/src/inbound-channel.js.map +1 -0
- package/dist/agent-runtime/src/index.d.ts +6 -5
- package/dist/agent-runtime/src/interrupts.cjs +45 -3
- package/dist/agent-runtime/src/interrupts.cjs.map +1 -1
- package/dist/agent-runtime/src/interrupts.d.ts +51 -4
- package/dist/agent-runtime/src/interrupts.js +46 -4
- package/dist/agent-runtime/src/interrupts.js.map +1 -1
- package/dist/agent-runtime/src/message-window.cjs +43 -5
- package/dist/agent-runtime/src/message-window.cjs.map +1 -1
- package/dist/agent-runtime/src/message-window.d.ts +26 -2
- package/dist/agent-runtime/src/message-window.js +44 -6
- package/dist/agent-runtime/src/message-window.js.map +1 -1
- package/dist/agent-runtime/src/offline-status.cjs.map +1 -1
- package/dist/agent-runtime/src/offline-status.d.ts +110 -4
- package/dist/agent-runtime/src/offline-status.js.map +1 -1
- package/dist/agent-runtime/src/runtime.d.ts +1 -0
- package/dist/agent-runtime/src/thread-handoff-flow-machine.d.ts +8 -8
- package/dist/agent-runtime/src/tool-metadata.cjs +16 -0
- package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.js +16 -0
- package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs +8 -16
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.d.ts +1 -9
- package/dist/agent-runtime/src/use-deep-agent-thread.js +9 -17
- package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs +86 -79
- package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-window-sync.d.ts +2 -27
- package/dist/agent-runtime/src/use-deep-agent-window-sync.js +89 -82
- package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
- package/dist/auth/AthenaSSOProvider.cjs +54 -12
- package/dist/auth/AthenaSSOProvider.cjs.map +1 -1
- package/dist/auth/AthenaSSOProvider.d.ts +23 -2
- package/dist/auth/AthenaSSOProvider.js +54 -12
- package/dist/auth/AthenaSSOProvider.js.map +1 -1
- package/dist/auth/index.cjs +2 -0
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/sign-back-in.cjs +141 -0
- package/dist/auth/sign-back-in.cjs.map +1 -0
- package/dist/auth/sign-back-in.d.ts +117 -0
- package/dist/auth/sign-back-in.js +141 -0
- package/dist/auth/sign-back-in.js.map +1 -0
- package/dist/chat/AthenaChat.cjs +7 -2
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.js +7 -2
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/StatewireApprovalCard.cjs +6 -2
- package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
- package/dist/chat/StatewireApprovalCard.d.ts +8 -1
- package/dist/chat/StatewireApprovalCard.js +7 -3
- package/dist/chat/StatewireApprovalCard.js.map +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.cjs +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.cjs.map +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.d.ts +6 -5
- package/dist/chat/super-grouping/CollapsibleGroup.js +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.js.map +1 -1
- package/dist/chat/super-grouping/earlier-steps-button.cjs +5 -6
- package/dist/chat/super-grouping/earlier-steps-button.cjs.map +1 -1
- package/dist/chat/super-grouping/earlier-steps-button.d.ts +6 -6
- package/dist/chat/super-grouping/earlier-steps-button.js +5 -6
- package/dist/chat/super-grouping/earlier-steps-button.js.map +1 -1
- package/dist/chat/super-grouping/toolkit-map.cjs +1 -0
- package/dist/chat/super-grouping/toolkit-map.cjs.map +1 -1
- package/dist/chat/super-grouping/toolkit-map.js +1 -0
- package/dist/chat/super-grouping/toolkit-map.js.map +1 -1
- package/dist/chat/super-grouping/use-turn-steps.cjs +3 -3
- package/dist/chat/super-grouping/use-turn-steps.js +1 -1
- package/dist/chat-ui/index.cjs +4 -0
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.d.ts +1 -0
- package/dist/chat-ui/index.js +5 -1
- package/dist/chat-ui/index.js.map +1 -1
- package/dist/chat-ui/messages/inbound-channel-message.cjs +219 -0
- package/dist/chat-ui/messages/inbound-channel-message.cjs.map +1 -0
- package/dist/chat-ui/messages/inbound-channel-message.d.ts +60 -0
- package/dist/chat-ui/messages/inbound-channel-message.js +219 -0
- package/dist/chat-ui/messages/inbound-channel-message.js.map +1 -0
- package/dist/chat-ui/messages/inbound-channel-reader.cjs +184 -0
- package/dist/chat-ui/messages/inbound-channel-reader.cjs.map +1 -0
- package/dist/chat-ui/messages/inbound-channel-reader.d.ts +65 -0
- package/dist/chat-ui/messages/inbound-channel-reader.js +184 -0
- package/dist/chat-ui/messages/inbound-channel-reader.js.map +1 -0
- package/dist/chat-ui/messages/message-components.cjs +18 -14
- package/dist/chat-ui/messages/message-components.cjs.map +1 -1
- package/dist/chat-ui/messages/message-components.d.ts +7 -0
- package/dist/chat-ui/messages/message-components.js +19 -15
- package/dist/chat-ui/messages/message-components.js.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.cjs +0 -4
- package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.d.ts +5 -7
- package/dist/chat-ui/thread/statewire-history-edge.js +1 -5
- package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
- package/dist/composer/sign-back-in-draft.cjs +62 -0
- package/dist/composer/sign-back-in-draft.cjs.map +1 -0
- package/dist/composer/sign-back-in-draft.d.ts +34 -0
- package/dist/composer/sign-back-in-draft.js +62 -0
- package/dist/composer/sign-back-in-draft.js.map +1 -0
- package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
- package/dist/diagnostics/athena-diagnostics.d.ts +6 -1
- package/dist/diagnostics/athena-diagnostics.js.map +1 -1
- package/dist/index.cjs +8 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/provider/AthenaProvider.cjs +60 -14
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.js +60 -14
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/provider/statewire-window-context.cjs.map +1 -1
- package/dist/provider/statewire-window-context.d.ts +8 -8
- package/dist/provider/statewire-window-context.js.map +1 -1
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/runtime/auth.cjs +11 -0
- package/dist/runtime/auth.cjs.map +1 -1
- package/dist/runtime/auth.d.ts +18 -1
- package/dist/runtime/auth.js +11 -0
- package/dist/runtime/auth.js.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs +0 -2
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.d.ts +5 -5
- package/dist/runtime/useAthenaStatewireRuntime.js +0 -2
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/agent-runtime/src/history-pages.cjs +0 -279
- package/dist/agent-runtime/src/history-pages.cjs.map +0 -1
- package/dist/agent-runtime/src/history-pages.d.ts +0 -252
- package/dist/agent-runtime/src/history-pages.js +0 -279
- package/dist/agent-runtime/src/history-pages.js.map +0 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const inboundChannel = require("../../agent-runtime/src/inbound-channel.cjs");
|
|
4
|
+
const InboundChannelKind = {
|
|
5
|
+
SLACK: "slack",
|
|
6
|
+
EMAIL: "email",
|
|
7
|
+
SMS: "sms",
|
|
8
|
+
TEAMS: "teams",
|
|
9
|
+
API: "api",
|
|
10
|
+
VOICE_MEMO: "voice_memo",
|
|
11
|
+
OTHER: "other"
|
|
12
|
+
};
|
|
13
|
+
const KIND_BY_CHANNEL = {
|
|
14
|
+
slack: InboundChannelKind.SLACK,
|
|
15
|
+
agent_slack: InboundChannelKind.SLACK,
|
|
16
|
+
email: InboundChannelKind.EMAIL,
|
|
17
|
+
agent_email: InboundChannelKind.EMAIL,
|
|
18
|
+
sms: InboundChannelKind.SMS,
|
|
19
|
+
agent_sms: InboundChannelKind.SMS,
|
|
20
|
+
sms_group: InboundChannelKind.SMS,
|
|
21
|
+
agent_sms_group: InboundChannelKind.SMS,
|
|
22
|
+
teams: InboundChannelKind.TEAMS,
|
|
23
|
+
agent_teams: InboundChannelKind.TEAMS,
|
|
24
|
+
programmatic: InboundChannelKind.API,
|
|
25
|
+
api: InboundChannelKind.API,
|
|
26
|
+
voice_memo: InboundChannelKind.VOICE_MEMO
|
|
27
|
+
};
|
|
28
|
+
const KIND_LABEL = {
|
|
29
|
+
slack: "Slack",
|
|
30
|
+
email: "Email",
|
|
31
|
+
sms: "Text message",
|
|
32
|
+
teams: "Microsoft Teams",
|
|
33
|
+
api: "API",
|
|
34
|
+
voice_memo: "Voice memo"
|
|
35
|
+
};
|
|
36
|
+
const LABEL_WORDS = {
|
|
37
|
+
api: "API",
|
|
38
|
+
cc: "CC",
|
|
39
|
+
id: "ID",
|
|
40
|
+
ids: "IDs",
|
|
41
|
+
sid: "SID",
|
|
42
|
+
sms: "SMS",
|
|
43
|
+
ts: "timestamp",
|
|
44
|
+
url: "URL"
|
|
45
|
+
};
|
|
46
|
+
function inboundContextFieldLabel(key) {
|
|
47
|
+
const words = key.split(/[_\s]+/).filter((word) => word.length > 0).map((word) => LABEL_WORDS[word.toLowerCase()] ?? word.toLowerCase());
|
|
48
|
+
const [first, ...rest] = words;
|
|
49
|
+
if (first === void 0) return key;
|
|
50
|
+
return [first.charAt(0).toUpperCase() + first.slice(1), ...rest].join(" ");
|
|
51
|
+
}
|
|
52
|
+
function field(key, value) {
|
|
53
|
+
return { key, label: inboundContextFieldLabel(key), value };
|
|
54
|
+
}
|
|
55
|
+
function contextFields(lines) {
|
|
56
|
+
const fields = [];
|
|
57
|
+
for (const line of lines.split("\n")) {
|
|
58
|
+
const separator = line.indexOf(":");
|
|
59
|
+
if (separator <= 0) continue;
|
|
60
|
+
const key = line.slice(0, separator).trim();
|
|
61
|
+
const value = line.slice(separator + 1).trim();
|
|
62
|
+
if (key && value) fields.push(field(key, value));
|
|
63
|
+
}
|
|
64
|
+
return fields;
|
|
65
|
+
}
|
|
66
|
+
function isRecord(value) {
|
|
67
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
68
|
+
}
|
|
69
|
+
function readString(record, key) {
|
|
70
|
+
const value = record[key];
|
|
71
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
72
|
+
}
|
|
73
|
+
function readScalar(value) {
|
|
74
|
+
if (typeof value === "string") return value.trim() || null;
|
|
75
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
76
|
+
if (typeof value === "boolean") return value ? "yes" : "no";
|
|
77
|
+
if (Array.isArray(value)) {
|
|
78
|
+
const items = value.filter((item) => typeof item === "string" && item.trim() !== "");
|
|
79
|
+
return items.length > 0 ? items.join(", ") : null;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const HIDDEN_DETAILS = /* @__PURE__ */ new Set(["body_text"]);
|
|
84
|
+
function inboundChannelKind(channel) {
|
|
85
|
+
return KIND_BY_CHANNEL[channel.trim().toLowerCase()] ?? InboundChannelKind.OTHER;
|
|
86
|
+
}
|
|
87
|
+
function humanize(value) {
|
|
88
|
+
const words = value.replace(/^agent_/, "").replace(/[-_.]+/g, " ").trim();
|
|
89
|
+
return words ? words.charAt(0).toUpperCase() + words.slice(1) : "Message";
|
|
90
|
+
}
|
|
91
|
+
function inboundChannelLabel(channel) {
|
|
92
|
+
const kind = inboundChannelKind(channel);
|
|
93
|
+
return kind === InboundChannelKind.OTHER ? humanize(channel) : KIND_LABEL[kind];
|
|
94
|
+
}
|
|
95
|
+
function formatInboundPhoneNumber(value) {
|
|
96
|
+
const match = /^\+1(\d{3})(\d{3})(\d{4})$/.exec(value.trim());
|
|
97
|
+
return match ? `+1 (${match[1]}) ${match[2]}-${match[3]}` : value;
|
|
98
|
+
}
|
|
99
|
+
const SLACK_PLACES = {
|
|
100
|
+
"direct message": "Direct message",
|
|
101
|
+
"group direct message": "Group DM",
|
|
102
|
+
"public channel": "Channel",
|
|
103
|
+
"private channel": "Private channel"
|
|
104
|
+
};
|
|
105
|
+
const TEAMS_PLACES = {
|
|
106
|
+
personal: "Personal chat",
|
|
107
|
+
groupchat: "Group chat",
|
|
108
|
+
channel: "Channel"
|
|
109
|
+
};
|
|
110
|
+
function factValue(facts, key) {
|
|
111
|
+
var _a;
|
|
112
|
+
return ((_a = facts.find((entry) => entry.key === key)) == null ? void 0 : _a.value) ?? null;
|
|
113
|
+
}
|
|
114
|
+
function slackPlace(facts) {
|
|
115
|
+
const named = /^(#[^\s(]+)/.exec(factValue(facts, "slack_channel") ?? "");
|
|
116
|
+
if (named == null ? void 0 : named[1]) return named[1];
|
|
117
|
+
const type = factValue(facts, "slack_channel_type");
|
|
118
|
+
return type ? SLACK_PLACES[type.toLowerCase()] ?? humanize(type) : null;
|
|
119
|
+
}
|
|
120
|
+
function teamsPlace(facts) {
|
|
121
|
+
const type = factValue(facts, "teams_conversation_type") ?? factValue(facts, "conversation_type");
|
|
122
|
+
return type ? TEAMS_PLACES[type.toLowerCase()] ?? humanize(type) : null;
|
|
123
|
+
}
|
|
124
|
+
function groupTextPlace(facts, channel) {
|
|
125
|
+
const isGroup = factValue(facts, "source") === inboundChannel.GROUP_TEXT_SOURCE || channel === "sms_group" || channel === "agent_sms_group" || factValue(facts, "group_thread_id") !== null;
|
|
126
|
+
if (!isGroup) return null;
|
|
127
|
+
const title = factValue(facts, "group_title");
|
|
128
|
+
return title ? `Group text · ${title}` : "Group text";
|
|
129
|
+
}
|
|
130
|
+
function readInboundChannelMessage({
|
|
131
|
+
custom,
|
|
132
|
+
text
|
|
133
|
+
}) {
|
|
134
|
+
const envelope = custom == null ? void 0 : custom[inboundChannel.INBOUND_CHANNEL_KEY];
|
|
135
|
+
if (!isRecord(envelope)) return null;
|
|
136
|
+
const channel = readString(envelope, "channel");
|
|
137
|
+
if (channel === null) return null;
|
|
138
|
+
const details = [];
|
|
139
|
+
if (isRecord(envelope.details)) {
|
|
140
|
+
for (const [key, value] of Object.entries(envelope.details)) {
|
|
141
|
+
if (HIDDEN_DETAILS.has(key)) continue;
|
|
142
|
+
const scalar = readScalar(value);
|
|
143
|
+
if (scalar !== null) details.push(field(key, scalar));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const senderName = readString(envelope, "sender_name");
|
|
147
|
+
const senderHandle = readString(envelope, "sender_handle");
|
|
148
|
+
const subject = readString(envelope, "subject");
|
|
149
|
+
const context = inboundChannel.leadingInboundContext(text);
|
|
150
|
+
const facts = context ? contextFields(context.lines) : [
|
|
151
|
+
field("source", inboundChannelLabel(channel)),
|
|
152
|
+
...senderName ? [field("sender_name", senderName)] : [],
|
|
153
|
+
...senderHandle ? [field("sender_handle", senderHandle)] : [],
|
|
154
|
+
...subject ? [field("subject", subject)] : [],
|
|
155
|
+
...details
|
|
156
|
+
];
|
|
157
|
+
const kind = inboundChannelKind(channel);
|
|
158
|
+
const known = [...facts, ...details];
|
|
159
|
+
let place = null;
|
|
160
|
+
if (kind === InboundChannelKind.SLACK) place = slackPlace(known);
|
|
161
|
+
else if (kind === InboundChannelKind.TEAMS) place = teamsPlace(known);
|
|
162
|
+
else if (kind === InboundChannelKind.SMS) place = groupTextPlace(known, channel);
|
|
163
|
+
return {
|
|
164
|
+
origin: {
|
|
165
|
+
kind,
|
|
166
|
+
channel,
|
|
167
|
+
label: inboundChannelLabel(channel),
|
|
168
|
+
senderName,
|
|
169
|
+
senderHandle: senderHandle !== null && kind === InboundChannelKind.SMS ? formatInboundPhoneNumber(senderHandle) : senderHandle,
|
|
170
|
+
place,
|
|
171
|
+
subject
|
|
172
|
+
},
|
|
173
|
+
facts,
|
|
174
|
+
rawContext: (context == null ? void 0 : context.raw) ?? null,
|
|
175
|
+
quoted: context ? inboundChannel.splitInboundBody(context).quoted : null
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
exports.InboundChannelKind = InboundChannelKind;
|
|
179
|
+
exports.formatInboundPhoneNumber = formatInboundPhoneNumber;
|
|
180
|
+
exports.inboundChannelKind = inboundChannelKind;
|
|
181
|
+
exports.inboundChannelLabel = inboundChannelLabel;
|
|
182
|
+
exports.inboundContextFieldLabel = inboundContextFieldLabel;
|
|
183
|
+
exports.readInboundChannelMessage = readInboundChannelMessage;
|
|
184
|
+
//# sourceMappingURL=inbound-channel-reader.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbound-channel-reader.cjs","sources":["../../../src/chat-ui/messages/inbound-channel-reader.ts"],"sourcesContent":["/**\n * A channel message read in full, for the line above its bubble\n * (`inbound-channel-message.tsx`): where it came from (channel, place), who\n * sent it (name, address or number), and every fact the agent received with\n * it, labelled for a person. The small reading every bubble needs (the words\n * without the routing block, the envelope check) lives in\n * `@athenaintel/agent-runtime`'s `inbound-channel.ts`.\n */\nimport {\n GROUP_TEXT_SOURCE,\n INBOUND_CHANNEL_KEY,\n leadingInboundContext,\n splitInboundBody,\n} from \"@athenaintel/agent-runtime\";\n\n/** Where a message came from, as a client draws it. */\nexport const InboundChannelKind = {\n SLACK: \"slack\",\n EMAIL: \"email\",\n SMS: \"sms\",\n TEAMS: \"teams\",\n API: \"api\",\n VOICE_MEMO: \"voice_memo\",\n OTHER: \"other\",\n} as const;\nexport type InboundChannelKind = (typeof InboundChannelKind)[keyof typeof InboundChannelKind];\n\n/** The envelope's `channel` values, bare and as the collab-agent routers name them. */\nconst KIND_BY_CHANNEL: Readonly<Record<string, InboundChannelKind>> = {\n slack: InboundChannelKind.SLACK,\n agent_slack: InboundChannelKind.SLACK,\n email: InboundChannelKind.EMAIL,\n agent_email: InboundChannelKind.EMAIL,\n sms: InboundChannelKind.SMS,\n agent_sms: InboundChannelKind.SMS,\n sms_group: InboundChannelKind.SMS,\n agent_sms_group: InboundChannelKind.SMS,\n teams: InboundChannelKind.TEAMS,\n agent_teams: InboundChannelKind.TEAMS,\n programmatic: InboundChannelKind.API,\n api: InboundChannelKind.API,\n voice_memo: InboundChannelKind.VOICE_MEMO,\n};\n\nconst KIND_LABEL: Readonly<Record<Exclude<InboundChannelKind, \"other\">, string>> = {\n slack: \"Slack\",\n email: \"Email\",\n sms: \"Text message\",\n teams: \"Microsoft Teams\",\n api: \"API\",\n voice_memo: \"Voice memo\",\n};\n\n/** One fact the agent read, labelled for a person. */\nexport interface InboundContextField {\n /** The key as the agent read it (`slack_channel`). */\n readonly key: string;\n /** The key for a person (`Slack channel`). */\n readonly label: string;\n readonly value: string;\n}\n\n/** Where a message came from and who sent it: the envelope, ready to draw. */\nexport interface InboundChannelOrigin {\n readonly kind: InboundChannelKind;\n /** The envelope's channel as stamped (`slack`, `sms`, `voice_memo`, …). */\n readonly channel: string;\n /** The channel as people name it: \"Slack\", \"Email\", \"Text message\", \"Microsoft Teams\". */\n readonly label: string;\n readonly senderName: string | null;\n /** An email address, or a phone number written for reading. */\n readonly senderHandle: string | null;\n /** Where on the channel it was said: \"#launch\", \"Direct message\", \"Group text · Family\". */\n readonly place: string | null;\n /** An email's subject; a voice memo's title. */\n readonly subject: string | null;\n}\n\n/** A channel message, read for its line. */\nexport interface InboundChannelMessage {\n readonly origin: InboundChannelOrigin;\n /** What the agent read with it: the routing block's facts, else the envelope's own. */\n readonly facts: readonly InboundContextField[];\n /** The block exactly as the agent read it; `null` when the text carried none. */\n readonly rawContext: string | null;\n /** Earlier messages the channel quoted for the agent (the Slack thread a mention came in). */\n readonly quoted: string | null;\n}\n\nconst LABEL_WORDS: Readonly<Record<string, string>> = {\n api: \"API\",\n cc: \"CC\",\n id: \"ID\",\n ids: \"IDs\",\n sid: \"SID\",\n sms: \"SMS\",\n ts: \"timestamp\",\n url: \"URL\",\n};\n\n/** A fact's key for a person: `slack_workspace_id` → \"Slack workspace ID\". */\nexport function inboundContextFieldLabel(key: string): string {\n const words = key\n .split(/[_\\s]+/)\n .filter((word) => word.length > 0)\n .map((word) => LABEL_WORDS[word.toLowerCase()] ?? word.toLowerCase());\n const [first, ...rest] = words;\n if (first === undefined) return key;\n return [first.charAt(0).toUpperCase() + first.slice(1), ...rest].join(\" \");\n}\n\nfunction field(key: string, value: string): InboundContextField {\n return { key, label: inboundContextFieldLabel(key), value };\n}\n\n/** The block's `key: value` lines, labelled, in order. */\nfunction contextFields(lines: string): InboundContextField[] {\n const fields: InboundContextField[] = [];\n for (const line of lines.split(\"\\n\")) {\n const separator = line.indexOf(\":\");\n if (separator <= 0) continue;\n const key = line.slice(0, separator).trim();\n const value = line.slice(separator + 1).trim();\n if (key && value) fields.push(field(key, value));\n }\n return fields;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readString(record: Record<string, unknown>, key: string): string | null {\n const value = record[key];\n return typeof value === \"string\" && value.trim().length > 0 ? value.trim() : null;\n}\n\nfunction readScalar(value: unknown): string | null {\n if (typeof value === \"string\") return value.trim() || null;\n if (typeof value === \"number\" && Number.isFinite(value)) return String(value);\n if (typeof value === \"boolean\") return value ? \"yes\" : \"no\";\n if (Array.isArray(value)) {\n const items = value.filter((item): item is string => typeof item === \"string\" && item.trim() !== \"\");\n return items.length > 0 ? items.join(\", \") : null;\n }\n return null;\n}\n\n/** Envelope details a person doesn't need repeated: an email's body, which is the message itself. */\nconst HIDDEN_DETAILS: ReadonlySet<string> = new Set([\"body_text\"]);\n\n/** The kind a client draws for an envelope's channel. */\nexport function inboundChannelKind(channel: string): InboundChannelKind {\n return KIND_BY_CHANNEL[channel.trim().toLowerCase()] ?? InboundChannelKind.OTHER;\n}\n\nfunction humanize(value: string): string {\n const words = value.replace(/^agent_/, \"\").replace(/[-_.]+/g, \" \").trim();\n return words ? words.charAt(0).toUpperCase() + words.slice(1) : \"Message\";\n}\n\n/** The channel as people name it: \"Slack\", \"Text message\"; another channel in its own words. */\nexport function inboundChannelLabel(channel: string): string {\n const kind = inboundChannelKind(channel);\n return kind === InboundChannelKind.OTHER ? humanize(channel) : KIND_LABEL[kind];\n}\n\n/**\n * A phone number written for reading: a North American `+1` number as\n * `+1 (224) 555-0100`; any other number as it came.\n */\nexport function formatInboundPhoneNumber(value: string): string {\n const match = /^\\+1(\\d{3})(\\d{3})(\\d{4})$/.exec(value.trim());\n return match ? `+1 (${match[1]}) ${match[2]}-${match[3]}` : value;\n}\n\nconst SLACK_PLACES: Readonly<Record<string, string>> = {\n \"direct message\": \"Direct message\",\n \"group direct message\": \"Group DM\",\n \"public channel\": \"Channel\",\n \"private channel\": \"Private channel\",\n};\n\nconst TEAMS_PLACES: Readonly<Record<string, string>> = {\n personal: \"Personal chat\",\n groupchat: \"Group chat\",\n channel: \"Channel\",\n};\n\nfunction factValue(facts: readonly InboundContextField[], key: string): string | null {\n return facts.find((entry) => entry.key === key)?.value ?? null;\n}\n\nfunction slackPlace(facts: readonly InboundContextField[]): string | null {\n const named = /^(#[^\\s(]+)/.exec(factValue(facts, \"slack_channel\") ?? \"\");\n if (named?.[1]) return named[1];\n const type = factValue(facts, \"slack_channel_type\");\n return type ? (SLACK_PLACES[type.toLowerCase()] ?? humanize(type)) : null;\n}\n\nfunction teamsPlace(facts: readonly InboundContextField[]): string | null {\n const type = factValue(facts, \"teams_conversation_type\") ?? factValue(facts, \"conversation_type\");\n return type ? (TEAMS_PLACES[type.toLowerCase()] ?? humanize(type)) : null;\n}\n\nfunction groupTextPlace(facts: readonly InboundContextField[], channel: string): string | null {\n const isGroup =\n factValue(facts, \"source\") === GROUP_TEXT_SOURCE ||\n channel === \"sms_group\" ||\n channel === \"agent_sms_group\" ||\n factValue(facts, \"group_thread_id\") !== null;\n if (!isGroup) return null;\n const title = factValue(facts, \"group_title\");\n return title ? `Group text · ${title}` : \"Group text\";\n}\n\n/**\n * A user turn read as a channel message, or `null` when it carries no\n * `inbound_channel` envelope in `custom` (an ordinary turn, tags a person\n * typed included). The facts are the routing block's, or the envelope's own\n * when the text starts with none.\n */\nexport function readInboundChannelMessage({\n custom,\n text,\n}: {\n custom: Record<string, unknown> | null | undefined;\n text: string;\n}): InboundChannelMessage | null {\n const envelope = custom?.[INBOUND_CHANNEL_KEY];\n if (!isRecord(envelope)) return null;\n const channel = readString(envelope, \"channel\");\n if (channel === null) return null;\n\n const details: InboundContextField[] = [];\n if (isRecord(envelope.details)) {\n for (const [key, value] of Object.entries(envelope.details)) {\n if (HIDDEN_DETAILS.has(key)) continue;\n const scalar = readScalar(value);\n if (scalar !== null) details.push(field(key, scalar));\n }\n }\n const senderName = readString(envelope, \"sender_name\");\n const senderHandle = readString(envelope, \"sender_handle\");\n const subject = readString(envelope, \"subject\");\n\n const context = leadingInboundContext(text);\n const facts: InboundContextField[] = context\n ? contextFields(context.lines)\n : [\n field(\"source\", inboundChannelLabel(channel)),\n ...(senderName ? [field(\"sender_name\", senderName)] : []),\n ...(senderHandle ? [field(\"sender_handle\", senderHandle)] : []),\n ...(subject ? [field(\"subject\", subject)] : []),\n ...details,\n ];\n\n const kind = inboundChannelKind(channel);\n // The block and the envelope's details name the same places; either serves.\n const known = [...facts, ...details];\n let place: string | null = null;\n if (kind === InboundChannelKind.SLACK) place = slackPlace(known);\n else if (kind === InboundChannelKind.TEAMS) place = teamsPlace(known);\n else if (kind === InboundChannelKind.SMS) place = groupTextPlace(known, channel);\n\n return {\n origin: {\n kind,\n channel,\n label: inboundChannelLabel(channel),\n senderName,\n senderHandle:\n senderHandle !== null && kind === InboundChannelKind.SMS\n ? formatInboundPhoneNumber(senderHandle)\n : senderHandle,\n place,\n subject,\n },\n facts,\n rawContext: context?.raw ?? null,\n quoted: context ? splitInboundBody(context).quoted : null,\n };\n}\n"],"names":["GROUP_TEXT_SOURCE","INBOUND_CHANNEL_KEY","leadingInboundContext","splitInboundBody"],"mappings":";;;AAgBO,MAAM,qBAAqB;AAAA,EAChC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AAAA,EACP,KAAK;AAAA,EACL,YAAY;AAAA,EACZ,OAAO;AACT;AAIA,MAAM,kBAAgE;AAAA,EACpE,OAAO,mBAAmB;AAAA,EAC1B,aAAa,mBAAmB;AAAA,EAChC,OAAO,mBAAmB;AAAA,EAC1B,aAAa,mBAAmB;AAAA,EAChC,KAAK,mBAAmB;AAAA,EACxB,WAAW,mBAAmB;AAAA,EAC9B,WAAW,mBAAmB;AAAA,EAC9B,iBAAiB,mBAAmB;AAAA,EACpC,OAAO,mBAAmB;AAAA,EAC1B,aAAa,mBAAmB;AAAA,EAChC,cAAc,mBAAmB;AAAA,EACjC,KAAK,mBAAmB;AAAA,EACxB,YAAY,mBAAmB;AACjC;AAEA,MAAM,aAA6E;AAAA,EACjF,OAAO;AAAA,EACP,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AAAA,EACP,KAAK;AAAA,EACL,YAAY;AACd;AAsCA,MAAM,cAAgD;AAAA,EACpD,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AACP;AAGO,SAAS,yBAAyB,KAAqB;AAC5D,QAAM,QAAQ,IACX,MAAM,QAAQ,EACd,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,EAChC,IAAI,CAAC,SAAS,YAAY,KAAK,aAAa,KAAK,KAAK,aAAa;AACtE,QAAM,CAAC,OAAO,GAAG,IAAI,IAAI;AACzB,MAAI,UAAU,OAAW,QAAO;AAChC,SAAO,CAAC,MAAM,OAAO,CAAC,EAAE,YAAA,IAAgB,MAAM,MAAM,CAAC,GAAG,GAAG,IAAI,EAAE,KAAK,GAAG;AAC3E;AAEA,SAAS,MAAM,KAAa,OAAoC;AAC9D,SAAO,EAAE,KAAK,OAAO,yBAAyB,GAAG,GAAG,MAAA;AACtD;AAGA,SAAS,cAAc,OAAsC;AAC3D,QAAM,SAAgC,CAAA;AACtC,aAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,UAAM,YAAY,KAAK,QAAQ,GAAG;AAClC,QAAI,aAAa,EAAG;AACpB,UAAM,MAAM,KAAK,MAAM,GAAG,SAAS,EAAE,KAAA;AACrC,UAAM,QAAQ,KAAK,MAAM,YAAY,CAAC,EAAE,KAAA;AACxC,QAAI,OAAO,MAAO,QAAO,KAAK,MAAM,KAAK,KAAK,CAAC;AAAA,EACjD;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,WAAW,QAAiC,KAA4B;AAC/E,QAAM,QAAQ,OAAO,GAAG;AACxB,SAAO,OAAO,UAAU,YAAY,MAAM,KAAA,EAAO,SAAS,IAAI,MAAM,KAAA,IAAS;AAC/E;AAEA,SAAS,WAAW,OAA+B;AACjD,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,UAAU;AACtD,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO,OAAO,KAAK;AAC5E,MAAI,OAAO,UAAU,UAAW,QAAO,QAAQ,QAAQ;AACvD,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,QAAQ,MAAM,OAAO,CAAC,SAAyB,OAAO,SAAS,YAAY,KAAK,KAAA,MAAW,EAAE;AACnG,WAAO,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAAA,EAC/C;AACA,SAAO;AACT;AAGA,MAAM,iBAAsC,oBAAI,IAAI,CAAC,WAAW,CAAC;AAG1D,SAAS,mBAAmB,SAAqC;AACtE,SAAO,gBAAgB,QAAQ,KAAA,EAAO,YAAA,CAAa,KAAK,mBAAmB;AAC7E;AAEA,SAAS,SAAS,OAAuB;AACvC,QAAM,QAAQ,MAAM,QAAQ,WAAW,EAAE,EAAE,QAAQ,WAAW,GAAG,EAAE,KAAA;AACnE,SAAO,QAAQ,MAAM,OAAO,CAAC,EAAE,gBAAgB,MAAM,MAAM,CAAC,IAAI;AAClE;AAGO,SAAS,oBAAoB,SAAyB;AAC3D,QAAM,OAAO,mBAAmB,OAAO;AACvC,SAAO,SAAS,mBAAmB,QAAQ,SAAS,OAAO,IAAI,WAAW,IAAI;AAChF;AAMO,SAAS,yBAAyB,OAAuB;AAC9D,QAAM,QAAQ,6BAA6B,KAAK,MAAM,MAAM;AAC5D,SAAO,QAAQ,OAAO,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK;AAC9D;AAEA,MAAM,eAAiD;AAAA,EACrD,kBAAkB;AAAA,EAClB,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,mBAAmB;AACrB;AAEA,MAAM,eAAiD;AAAA,EACrD,UAAU;AAAA,EACV,WAAW;AAAA,EACX,SAAS;AACX;AAEA,SAAS,UAAU,OAAuC,KAA4B;;AACpF,WAAO,WAAM,KAAK,CAAC,UAAU,MAAM,QAAQ,GAAG,MAAvC,mBAA0C,UAAS;AAC5D;AAEA,SAAS,WAAW,OAAsD;AACxE,QAAM,QAAQ,cAAc,KAAK,UAAU,OAAO,eAAe,KAAK,EAAE;AACxE,MAAI,+BAAQ,GAAI,QAAO,MAAM,CAAC;AAC9B,QAAM,OAAO,UAAU,OAAO,oBAAoB;AAClD,SAAO,OAAQ,aAAa,KAAK,YAAA,CAAa,KAAK,SAAS,IAAI,IAAK;AACvE;AAEA,SAAS,WAAW,OAAsD;AACxE,QAAM,OAAO,UAAU,OAAO,yBAAyB,KAAK,UAAU,OAAO,mBAAmB;AAChG,SAAO,OAAQ,aAAa,KAAK,YAAA,CAAa,KAAK,SAAS,IAAI,IAAK;AACvE;AAEA,SAAS,eAAe,OAAuC,SAAgC;AAC7F,QAAM,UACJ,UAAU,OAAO,QAAQ,MAAMA,eAAAA,qBAC/B,YAAY,eACZ,YAAY,qBACZ,UAAU,OAAO,iBAAiB,MAAM;AAC1C,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAQ,UAAU,OAAO,aAAa;AAC5C,SAAO,QAAQ,gBAAgB,KAAK,KAAK;AAC3C;AAQO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AACF,GAGiC;AAC/B,QAAM,WAAW,iCAASC;AAC1B,MAAI,CAAC,SAAS,QAAQ,EAAG,QAAO;AAChC,QAAM,UAAU,WAAW,UAAU,SAAS;AAC9C,MAAI,YAAY,KAAM,QAAO;AAE7B,QAAM,UAAiC,CAAA;AACvC,MAAI,SAAS,SAAS,OAAO,GAAG;AAC9B,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,OAAO,GAAG;AAC3D,UAAI,eAAe,IAAI,GAAG,EAAG;AAC7B,YAAM,SAAS,WAAW,KAAK;AAC/B,UAAI,WAAW,KAAM,SAAQ,KAAK,MAAM,KAAK,MAAM,CAAC;AAAA,IACtD;AAAA,EACF;AACA,QAAM,aAAa,WAAW,UAAU,aAAa;AACrD,QAAM,eAAe,WAAW,UAAU,eAAe;AACzD,QAAM,UAAU,WAAW,UAAU,SAAS;AAE9C,QAAM,UAAUC,eAAAA,sBAAsB,IAAI;AAC1C,QAAM,QAA+B,UACjC,cAAc,QAAQ,KAAK,IAC3B;AAAA,IACE,MAAM,UAAU,oBAAoB,OAAO,CAAC;AAAA,IAC5C,GAAI,aAAa,CAAC,MAAM,eAAe,UAAU,CAAC,IAAI,CAAA;AAAA,IACtD,GAAI,eAAe,CAAC,MAAM,iBAAiB,YAAY,CAAC,IAAI,CAAA;AAAA,IAC5D,GAAI,UAAU,CAAC,MAAM,WAAW,OAAO,CAAC,IAAI,CAAA;AAAA,IAC5C,GAAG;AAAA,EAAA;AAGT,QAAM,OAAO,mBAAmB,OAAO;AAEvC,QAAM,QAAQ,CAAC,GAAG,OAAO,GAAG,OAAO;AACnC,MAAI,QAAuB;AAC3B,MAAI,SAAS,mBAAmB,MAAO,SAAQ,WAAW,KAAK;AAAA,WACtD,SAAS,mBAAmB,MAAO,SAAQ,WAAW,KAAK;AAAA,WAC3D,SAAS,mBAAmB,IAAK,SAAQ,eAAe,OAAO,OAAO;AAE/E,SAAO;AAAA,IACL,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA,OAAO,oBAAoB,OAAO;AAAA,MAClC;AAAA,MACA,cACE,iBAAiB,QAAQ,SAAS,mBAAmB,MACjD,yBAAyB,YAAY,IACrC;AAAA,MACN;AAAA,MACA;AAAA,IAAA;AAAA,IAEF;AAAA,IACA,aAAY,mCAAS,QAAO;AAAA,IAC5B,QAAQ,UAAUC,eAAAA,iBAAiB,OAAO,EAAE,SAAS;AAAA,EAAA;AAEzD;;;;;;;"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Where a message came from, as a client draws it. */
|
|
2
|
+
export declare const InboundChannelKind: {
|
|
3
|
+
readonly SLACK: "slack";
|
|
4
|
+
readonly EMAIL: "email";
|
|
5
|
+
readonly SMS: "sms";
|
|
6
|
+
readonly TEAMS: "teams";
|
|
7
|
+
readonly API: "api";
|
|
8
|
+
readonly VOICE_MEMO: "voice_memo";
|
|
9
|
+
readonly OTHER: "other";
|
|
10
|
+
};
|
|
11
|
+
export type InboundChannelKind = (typeof InboundChannelKind)[keyof typeof InboundChannelKind];
|
|
12
|
+
/** One fact the agent read, labelled for a person. */
|
|
13
|
+
export interface InboundContextField {
|
|
14
|
+
/** The key as the agent read it (`slack_channel`). */
|
|
15
|
+
readonly key: string;
|
|
16
|
+
/** The key for a person (`Slack channel`). */
|
|
17
|
+
readonly label: string;
|
|
18
|
+
readonly value: string;
|
|
19
|
+
}
|
|
20
|
+
/** Where a message came from and who sent it: the envelope, ready to draw. */
|
|
21
|
+
export interface InboundChannelOrigin {
|
|
22
|
+
readonly kind: InboundChannelKind;
|
|
23
|
+
/** The envelope's channel as stamped (`slack`, `sms`, `voice_memo`, …). */
|
|
24
|
+
readonly channel: string;
|
|
25
|
+
/** The channel as people name it: "Slack", "Email", "Text message", "Microsoft Teams". */
|
|
26
|
+
readonly label: string;
|
|
27
|
+
readonly senderName: string | null;
|
|
28
|
+
/** An email address, or a phone number written for reading. */
|
|
29
|
+
readonly senderHandle: string | null;
|
|
30
|
+
/** Where on the channel it was said: "#launch", "Direct message", "Group text · Family". */
|
|
31
|
+
readonly place: string | null;
|
|
32
|
+
/** An email's subject; a voice memo's title. */
|
|
33
|
+
readonly subject: string | null;
|
|
34
|
+
}
|
|
35
|
+
/** A channel message, read for its line. */
|
|
36
|
+
export interface InboundChannelMessage {
|
|
37
|
+
readonly origin: InboundChannelOrigin;
|
|
38
|
+
/** What the agent read with it: the routing block's facts, else the envelope's own. */
|
|
39
|
+
readonly facts: readonly InboundContextField[];
|
|
40
|
+
/** The block exactly as the agent read it; `null` when the text carried none. */
|
|
41
|
+
readonly rawContext: string | null;
|
|
42
|
+
/** Earlier messages the channel quoted for the agent (the Slack thread a mention came in). */
|
|
43
|
+
readonly quoted: string | null;
|
|
44
|
+
}
|
|
45
|
+
/** A fact's key for a person: `slack_workspace_id` → "Slack workspace ID". */
|
|
46
|
+
export declare function inboundContextFieldLabel(key: string): string;
|
|
47
|
+
/** The kind a client draws for an envelope's channel. */
|
|
48
|
+
export declare function inboundChannelKind(channel: string): InboundChannelKind;
|
|
49
|
+
/** The channel as people name it: "Slack", "Text message"; another channel in its own words. */
|
|
50
|
+
export declare function inboundChannelLabel(channel: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* A phone number written for reading: a North American `+1` number as
|
|
53
|
+
* `+1 (224) 555-0100`; any other number as it came.
|
|
54
|
+
*/
|
|
55
|
+
export declare function formatInboundPhoneNumber(value: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* A user turn read as a channel message, or `null` when it carries no
|
|
58
|
+
* `inbound_channel` envelope in `custom` (an ordinary turn, tags a person
|
|
59
|
+
* typed included). The facts are the routing block's, or the envelope's own
|
|
60
|
+
* when the text starts with none.
|
|
61
|
+
*/
|
|
62
|
+
export declare function readInboundChannelMessage({ custom, text, }: {
|
|
63
|
+
custom: Record<string, unknown> | null | undefined;
|
|
64
|
+
text: string;
|
|
65
|
+
}): InboundChannelMessage | null;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { INBOUND_CHANNEL_KEY, leadingInboundContext, splitInboundBody, GROUP_TEXT_SOURCE } from "../../agent-runtime/src/inbound-channel.js";
|
|
2
|
+
const InboundChannelKind = {
|
|
3
|
+
SLACK: "slack",
|
|
4
|
+
EMAIL: "email",
|
|
5
|
+
SMS: "sms",
|
|
6
|
+
TEAMS: "teams",
|
|
7
|
+
API: "api",
|
|
8
|
+
VOICE_MEMO: "voice_memo",
|
|
9
|
+
OTHER: "other"
|
|
10
|
+
};
|
|
11
|
+
const KIND_BY_CHANNEL = {
|
|
12
|
+
slack: InboundChannelKind.SLACK,
|
|
13
|
+
agent_slack: InboundChannelKind.SLACK,
|
|
14
|
+
email: InboundChannelKind.EMAIL,
|
|
15
|
+
agent_email: InboundChannelKind.EMAIL,
|
|
16
|
+
sms: InboundChannelKind.SMS,
|
|
17
|
+
agent_sms: InboundChannelKind.SMS,
|
|
18
|
+
sms_group: InboundChannelKind.SMS,
|
|
19
|
+
agent_sms_group: InboundChannelKind.SMS,
|
|
20
|
+
teams: InboundChannelKind.TEAMS,
|
|
21
|
+
agent_teams: InboundChannelKind.TEAMS,
|
|
22
|
+
programmatic: InboundChannelKind.API,
|
|
23
|
+
api: InboundChannelKind.API,
|
|
24
|
+
voice_memo: InboundChannelKind.VOICE_MEMO
|
|
25
|
+
};
|
|
26
|
+
const KIND_LABEL = {
|
|
27
|
+
slack: "Slack",
|
|
28
|
+
email: "Email",
|
|
29
|
+
sms: "Text message",
|
|
30
|
+
teams: "Microsoft Teams",
|
|
31
|
+
api: "API",
|
|
32
|
+
voice_memo: "Voice memo"
|
|
33
|
+
};
|
|
34
|
+
const LABEL_WORDS = {
|
|
35
|
+
api: "API",
|
|
36
|
+
cc: "CC",
|
|
37
|
+
id: "ID",
|
|
38
|
+
ids: "IDs",
|
|
39
|
+
sid: "SID",
|
|
40
|
+
sms: "SMS",
|
|
41
|
+
ts: "timestamp",
|
|
42
|
+
url: "URL"
|
|
43
|
+
};
|
|
44
|
+
function inboundContextFieldLabel(key) {
|
|
45
|
+
const words = key.split(/[_\s]+/).filter((word) => word.length > 0).map((word) => LABEL_WORDS[word.toLowerCase()] ?? word.toLowerCase());
|
|
46
|
+
const [first, ...rest] = words;
|
|
47
|
+
if (first === void 0) return key;
|
|
48
|
+
return [first.charAt(0).toUpperCase() + first.slice(1), ...rest].join(" ");
|
|
49
|
+
}
|
|
50
|
+
function field(key, value) {
|
|
51
|
+
return { key, label: inboundContextFieldLabel(key), value };
|
|
52
|
+
}
|
|
53
|
+
function contextFields(lines) {
|
|
54
|
+
const fields = [];
|
|
55
|
+
for (const line of lines.split("\n")) {
|
|
56
|
+
const separator = line.indexOf(":");
|
|
57
|
+
if (separator <= 0) continue;
|
|
58
|
+
const key = line.slice(0, separator).trim();
|
|
59
|
+
const value = line.slice(separator + 1).trim();
|
|
60
|
+
if (key && value) fields.push(field(key, value));
|
|
61
|
+
}
|
|
62
|
+
return fields;
|
|
63
|
+
}
|
|
64
|
+
function isRecord(value) {
|
|
65
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66
|
+
}
|
|
67
|
+
function readString(record, key) {
|
|
68
|
+
const value = record[key];
|
|
69
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
70
|
+
}
|
|
71
|
+
function readScalar(value) {
|
|
72
|
+
if (typeof value === "string") return value.trim() || null;
|
|
73
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
74
|
+
if (typeof value === "boolean") return value ? "yes" : "no";
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
const items = value.filter((item) => typeof item === "string" && item.trim() !== "");
|
|
77
|
+
return items.length > 0 ? items.join(", ") : null;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const HIDDEN_DETAILS = /* @__PURE__ */ new Set(["body_text"]);
|
|
82
|
+
function inboundChannelKind(channel) {
|
|
83
|
+
return KIND_BY_CHANNEL[channel.trim().toLowerCase()] ?? InboundChannelKind.OTHER;
|
|
84
|
+
}
|
|
85
|
+
function humanize(value) {
|
|
86
|
+
const words = value.replace(/^agent_/, "").replace(/[-_.]+/g, " ").trim();
|
|
87
|
+
return words ? words.charAt(0).toUpperCase() + words.slice(1) : "Message";
|
|
88
|
+
}
|
|
89
|
+
function inboundChannelLabel(channel) {
|
|
90
|
+
const kind = inboundChannelKind(channel);
|
|
91
|
+
return kind === InboundChannelKind.OTHER ? humanize(channel) : KIND_LABEL[kind];
|
|
92
|
+
}
|
|
93
|
+
function formatInboundPhoneNumber(value) {
|
|
94
|
+
const match = /^\+1(\d{3})(\d{3})(\d{4})$/.exec(value.trim());
|
|
95
|
+
return match ? `+1 (${match[1]}) ${match[2]}-${match[3]}` : value;
|
|
96
|
+
}
|
|
97
|
+
const SLACK_PLACES = {
|
|
98
|
+
"direct message": "Direct message",
|
|
99
|
+
"group direct message": "Group DM",
|
|
100
|
+
"public channel": "Channel",
|
|
101
|
+
"private channel": "Private channel"
|
|
102
|
+
};
|
|
103
|
+
const TEAMS_PLACES = {
|
|
104
|
+
personal: "Personal chat",
|
|
105
|
+
groupchat: "Group chat",
|
|
106
|
+
channel: "Channel"
|
|
107
|
+
};
|
|
108
|
+
function factValue(facts, key) {
|
|
109
|
+
var _a;
|
|
110
|
+
return ((_a = facts.find((entry) => entry.key === key)) == null ? void 0 : _a.value) ?? null;
|
|
111
|
+
}
|
|
112
|
+
function slackPlace(facts) {
|
|
113
|
+
const named = /^(#[^\s(]+)/.exec(factValue(facts, "slack_channel") ?? "");
|
|
114
|
+
if (named == null ? void 0 : named[1]) return named[1];
|
|
115
|
+
const type = factValue(facts, "slack_channel_type");
|
|
116
|
+
return type ? SLACK_PLACES[type.toLowerCase()] ?? humanize(type) : null;
|
|
117
|
+
}
|
|
118
|
+
function teamsPlace(facts) {
|
|
119
|
+
const type = factValue(facts, "teams_conversation_type") ?? factValue(facts, "conversation_type");
|
|
120
|
+
return type ? TEAMS_PLACES[type.toLowerCase()] ?? humanize(type) : null;
|
|
121
|
+
}
|
|
122
|
+
function groupTextPlace(facts, channel) {
|
|
123
|
+
const isGroup = factValue(facts, "source") === GROUP_TEXT_SOURCE || channel === "sms_group" || channel === "agent_sms_group" || factValue(facts, "group_thread_id") !== null;
|
|
124
|
+
if (!isGroup) return null;
|
|
125
|
+
const title = factValue(facts, "group_title");
|
|
126
|
+
return title ? `Group text · ${title}` : "Group text";
|
|
127
|
+
}
|
|
128
|
+
function readInboundChannelMessage({
|
|
129
|
+
custom,
|
|
130
|
+
text
|
|
131
|
+
}) {
|
|
132
|
+
const envelope = custom == null ? void 0 : custom[INBOUND_CHANNEL_KEY];
|
|
133
|
+
if (!isRecord(envelope)) return null;
|
|
134
|
+
const channel = readString(envelope, "channel");
|
|
135
|
+
if (channel === null) return null;
|
|
136
|
+
const details = [];
|
|
137
|
+
if (isRecord(envelope.details)) {
|
|
138
|
+
for (const [key, value] of Object.entries(envelope.details)) {
|
|
139
|
+
if (HIDDEN_DETAILS.has(key)) continue;
|
|
140
|
+
const scalar = readScalar(value);
|
|
141
|
+
if (scalar !== null) details.push(field(key, scalar));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const senderName = readString(envelope, "sender_name");
|
|
145
|
+
const senderHandle = readString(envelope, "sender_handle");
|
|
146
|
+
const subject = readString(envelope, "subject");
|
|
147
|
+
const context = leadingInboundContext(text);
|
|
148
|
+
const facts = context ? contextFields(context.lines) : [
|
|
149
|
+
field("source", inboundChannelLabel(channel)),
|
|
150
|
+
...senderName ? [field("sender_name", senderName)] : [],
|
|
151
|
+
...senderHandle ? [field("sender_handle", senderHandle)] : [],
|
|
152
|
+
...subject ? [field("subject", subject)] : [],
|
|
153
|
+
...details
|
|
154
|
+
];
|
|
155
|
+
const kind = inboundChannelKind(channel);
|
|
156
|
+
const known = [...facts, ...details];
|
|
157
|
+
let place = null;
|
|
158
|
+
if (kind === InboundChannelKind.SLACK) place = slackPlace(known);
|
|
159
|
+
else if (kind === InboundChannelKind.TEAMS) place = teamsPlace(known);
|
|
160
|
+
else if (kind === InboundChannelKind.SMS) place = groupTextPlace(known, channel);
|
|
161
|
+
return {
|
|
162
|
+
origin: {
|
|
163
|
+
kind,
|
|
164
|
+
channel,
|
|
165
|
+
label: inboundChannelLabel(channel),
|
|
166
|
+
senderName,
|
|
167
|
+
senderHandle: senderHandle !== null && kind === InboundChannelKind.SMS ? formatInboundPhoneNumber(senderHandle) : senderHandle,
|
|
168
|
+
place,
|
|
169
|
+
subject
|
|
170
|
+
},
|
|
171
|
+
facts,
|
|
172
|
+
rawContext: (context == null ? void 0 : context.raw) ?? null,
|
|
173
|
+
quoted: context ? splitInboundBody(context).quoted : null
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
export {
|
|
177
|
+
InboundChannelKind,
|
|
178
|
+
formatInboundPhoneNumber,
|
|
179
|
+
inboundChannelKind,
|
|
180
|
+
inboundChannelLabel,
|
|
181
|
+
inboundContextFieldLabel,
|
|
182
|
+
readInboundChannelMessage
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=inbound-channel-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbound-channel-reader.js","sources":["../../../src/chat-ui/messages/inbound-channel-reader.ts"],"sourcesContent":["/**\n * A channel message read in full, for the line above its bubble\n * (`inbound-channel-message.tsx`): where it came from (channel, place), who\n * sent it (name, address or number), and every fact the agent received with\n * it, labelled for a person. The small reading every bubble needs (the words\n * without the routing block, the envelope check) lives in\n * `@athenaintel/agent-runtime`'s `inbound-channel.ts`.\n */\nimport {\n GROUP_TEXT_SOURCE,\n INBOUND_CHANNEL_KEY,\n leadingInboundContext,\n splitInboundBody,\n} from \"@athenaintel/agent-runtime\";\n\n/** Where a message came from, as a client draws it. */\nexport const InboundChannelKind = {\n SLACK: \"slack\",\n EMAIL: \"email\",\n SMS: \"sms\",\n TEAMS: \"teams\",\n API: \"api\",\n VOICE_MEMO: \"voice_memo\",\n OTHER: \"other\",\n} as const;\nexport type InboundChannelKind = (typeof InboundChannelKind)[keyof typeof InboundChannelKind];\n\n/** The envelope's `channel` values, bare and as the collab-agent routers name them. */\nconst KIND_BY_CHANNEL: Readonly<Record<string, InboundChannelKind>> = {\n slack: InboundChannelKind.SLACK,\n agent_slack: InboundChannelKind.SLACK,\n email: InboundChannelKind.EMAIL,\n agent_email: InboundChannelKind.EMAIL,\n sms: InboundChannelKind.SMS,\n agent_sms: InboundChannelKind.SMS,\n sms_group: InboundChannelKind.SMS,\n agent_sms_group: InboundChannelKind.SMS,\n teams: InboundChannelKind.TEAMS,\n agent_teams: InboundChannelKind.TEAMS,\n programmatic: InboundChannelKind.API,\n api: InboundChannelKind.API,\n voice_memo: InboundChannelKind.VOICE_MEMO,\n};\n\nconst KIND_LABEL: Readonly<Record<Exclude<InboundChannelKind, \"other\">, string>> = {\n slack: \"Slack\",\n email: \"Email\",\n sms: \"Text message\",\n teams: \"Microsoft Teams\",\n api: \"API\",\n voice_memo: \"Voice memo\",\n};\n\n/** One fact the agent read, labelled for a person. */\nexport interface InboundContextField {\n /** The key as the agent read it (`slack_channel`). */\n readonly key: string;\n /** The key for a person (`Slack channel`). */\n readonly label: string;\n readonly value: string;\n}\n\n/** Where a message came from and who sent it: the envelope, ready to draw. */\nexport interface InboundChannelOrigin {\n readonly kind: InboundChannelKind;\n /** The envelope's channel as stamped (`slack`, `sms`, `voice_memo`, …). */\n readonly channel: string;\n /** The channel as people name it: \"Slack\", \"Email\", \"Text message\", \"Microsoft Teams\". */\n readonly label: string;\n readonly senderName: string | null;\n /** An email address, or a phone number written for reading. */\n readonly senderHandle: string | null;\n /** Where on the channel it was said: \"#launch\", \"Direct message\", \"Group text · Family\". */\n readonly place: string | null;\n /** An email's subject; a voice memo's title. */\n readonly subject: string | null;\n}\n\n/** A channel message, read for its line. */\nexport interface InboundChannelMessage {\n readonly origin: InboundChannelOrigin;\n /** What the agent read with it: the routing block's facts, else the envelope's own. */\n readonly facts: readonly InboundContextField[];\n /** The block exactly as the agent read it; `null` when the text carried none. */\n readonly rawContext: string | null;\n /** Earlier messages the channel quoted for the agent (the Slack thread a mention came in). */\n readonly quoted: string | null;\n}\n\nconst LABEL_WORDS: Readonly<Record<string, string>> = {\n api: \"API\",\n cc: \"CC\",\n id: \"ID\",\n ids: \"IDs\",\n sid: \"SID\",\n sms: \"SMS\",\n ts: \"timestamp\",\n url: \"URL\",\n};\n\n/** A fact's key for a person: `slack_workspace_id` → \"Slack workspace ID\". */\nexport function inboundContextFieldLabel(key: string): string {\n const words = key\n .split(/[_\\s]+/)\n .filter((word) => word.length > 0)\n .map((word) => LABEL_WORDS[word.toLowerCase()] ?? word.toLowerCase());\n const [first, ...rest] = words;\n if (first === undefined) return key;\n return [first.charAt(0).toUpperCase() + first.slice(1), ...rest].join(\" \");\n}\n\nfunction field(key: string, value: string): InboundContextField {\n return { key, label: inboundContextFieldLabel(key), value };\n}\n\n/** The block's `key: value` lines, labelled, in order. */\nfunction contextFields(lines: string): InboundContextField[] {\n const fields: InboundContextField[] = [];\n for (const line of lines.split(\"\\n\")) {\n const separator = line.indexOf(\":\");\n if (separator <= 0) continue;\n const key = line.slice(0, separator).trim();\n const value = line.slice(separator + 1).trim();\n if (key && value) fields.push(field(key, value));\n }\n return fields;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readString(record: Record<string, unknown>, key: string): string | null {\n const value = record[key];\n return typeof value === \"string\" && value.trim().length > 0 ? value.trim() : null;\n}\n\nfunction readScalar(value: unknown): string | null {\n if (typeof value === \"string\") return value.trim() || null;\n if (typeof value === \"number\" && Number.isFinite(value)) return String(value);\n if (typeof value === \"boolean\") return value ? \"yes\" : \"no\";\n if (Array.isArray(value)) {\n const items = value.filter((item): item is string => typeof item === \"string\" && item.trim() !== \"\");\n return items.length > 0 ? items.join(\", \") : null;\n }\n return null;\n}\n\n/** Envelope details a person doesn't need repeated: an email's body, which is the message itself. */\nconst HIDDEN_DETAILS: ReadonlySet<string> = new Set([\"body_text\"]);\n\n/** The kind a client draws for an envelope's channel. */\nexport function inboundChannelKind(channel: string): InboundChannelKind {\n return KIND_BY_CHANNEL[channel.trim().toLowerCase()] ?? InboundChannelKind.OTHER;\n}\n\nfunction humanize(value: string): string {\n const words = value.replace(/^agent_/, \"\").replace(/[-_.]+/g, \" \").trim();\n return words ? words.charAt(0).toUpperCase() + words.slice(1) : \"Message\";\n}\n\n/** The channel as people name it: \"Slack\", \"Text message\"; another channel in its own words. */\nexport function inboundChannelLabel(channel: string): string {\n const kind = inboundChannelKind(channel);\n return kind === InboundChannelKind.OTHER ? humanize(channel) : KIND_LABEL[kind];\n}\n\n/**\n * A phone number written for reading: a North American `+1` number as\n * `+1 (224) 555-0100`; any other number as it came.\n */\nexport function formatInboundPhoneNumber(value: string): string {\n const match = /^\\+1(\\d{3})(\\d{3})(\\d{4})$/.exec(value.trim());\n return match ? `+1 (${match[1]}) ${match[2]}-${match[3]}` : value;\n}\n\nconst SLACK_PLACES: Readonly<Record<string, string>> = {\n \"direct message\": \"Direct message\",\n \"group direct message\": \"Group DM\",\n \"public channel\": \"Channel\",\n \"private channel\": \"Private channel\",\n};\n\nconst TEAMS_PLACES: Readonly<Record<string, string>> = {\n personal: \"Personal chat\",\n groupchat: \"Group chat\",\n channel: \"Channel\",\n};\n\nfunction factValue(facts: readonly InboundContextField[], key: string): string | null {\n return facts.find((entry) => entry.key === key)?.value ?? null;\n}\n\nfunction slackPlace(facts: readonly InboundContextField[]): string | null {\n const named = /^(#[^\\s(]+)/.exec(factValue(facts, \"slack_channel\") ?? \"\");\n if (named?.[1]) return named[1];\n const type = factValue(facts, \"slack_channel_type\");\n return type ? (SLACK_PLACES[type.toLowerCase()] ?? humanize(type)) : null;\n}\n\nfunction teamsPlace(facts: readonly InboundContextField[]): string | null {\n const type = factValue(facts, \"teams_conversation_type\") ?? factValue(facts, \"conversation_type\");\n return type ? (TEAMS_PLACES[type.toLowerCase()] ?? humanize(type)) : null;\n}\n\nfunction groupTextPlace(facts: readonly InboundContextField[], channel: string): string | null {\n const isGroup =\n factValue(facts, \"source\") === GROUP_TEXT_SOURCE ||\n channel === \"sms_group\" ||\n channel === \"agent_sms_group\" ||\n factValue(facts, \"group_thread_id\") !== null;\n if (!isGroup) return null;\n const title = factValue(facts, \"group_title\");\n return title ? `Group text · ${title}` : \"Group text\";\n}\n\n/**\n * A user turn read as a channel message, or `null` when it carries no\n * `inbound_channel` envelope in `custom` (an ordinary turn, tags a person\n * typed included). The facts are the routing block's, or the envelope's own\n * when the text starts with none.\n */\nexport function readInboundChannelMessage({\n custom,\n text,\n}: {\n custom: Record<string, unknown> | null | undefined;\n text: string;\n}): InboundChannelMessage | null {\n const envelope = custom?.[INBOUND_CHANNEL_KEY];\n if (!isRecord(envelope)) return null;\n const channel = readString(envelope, \"channel\");\n if (channel === null) return null;\n\n const details: InboundContextField[] = [];\n if (isRecord(envelope.details)) {\n for (const [key, value] of Object.entries(envelope.details)) {\n if (HIDDEN_DETAILS.has(key)) continue;\n const scalar = readScalar(value);\n if (scalar !== null) details.push(field(key, scalar));\n }\n }\n const senderName = readString(envelope, \"sender_name\");\n const senderHandle = readString(envelope, \"sender_handle\");\n const subject = readString(envelope, \"subject\");\n\n const context = leadingInboundContext(text);\n const facts: InboundContextField[] = context\n ? contextFields(context.lines)\n : [\n field(\"source\", inboundChannelLabel(channel)),\n ...(senderName ? [field(\"sender_name\", senderName)] : []),\n ...(senderHandle ? [field(\"sender_handle\", senderHandle)] : []),\n ...(subject ? [field(\"subject\", subject)] : []),\n ...details,\n ];\n\n const kind = inboundChannelKind(channel);\n // The block and the envelope's details name the same places; either serves.\n const known = [...facts, ...details];\n let place: string | null = null;\n if (kind === InboundChannelKind.SLACK) place = slackPlace(known);\n else if (kind === InboundChannelKind.TEAMS) place = teamsPlace(known);\n else if (kind === InboundChannelKind.SMS) place = groupTextPlace(known, channel);\n\n return {\n origin: {\n kind,\n channel,\n label: inboundChannelLabel(channel),\n senderName,\n senderHandle:\n senderHandle !== null && kind === InboundChannelKind.SMS\n ? formatInboundPhoneNumber(senderHandle)\n : senderHandle,\n place,\n subject,\n },\n facts,\n rawContext: context?.raw ?? null,\n quoted: context ? splitInboundBody(context).quoted : null,\n };\n}\n"],"names":[],"mappings":";AAgBO,MAAM,qBAAqB;AAAA,EAChC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AAAA,EACP,KAAK;AAAA,EACL,YAAY;AAAA,EACZ,OAAO;AACT;AAIA,MAAM,kBAAgE;AAAA,EACpE,OAAO,mBAAmB;AAAA,EAC1B,aAAa,mBAAmB;AAAA,EAChC,OAAO,mBAAmB;AAAA,EAC1B,aAAa,mBAAmB;AAAA,EAChC,KAAK,mBAAmB;AAAA,EACxB,WAAW,mBAAmB;AAAA,EAC9B,WAAW,mBAAmB;AAAA,EAC9B,iBAAiB,mBAAmB;AAAA,EACpC,OAAO,mBAAmB;AAAA,EAC1B,aAAa,mBAAmB;AAAA,EAChC,cAAc,mBAAmB;AAAA,EACjC,KAAK,mBAAmB;AAAA,EACxB,YAAY,mBAAmB;AACjC;AAEA,MAAM,aAA6E;AAAA,EACjF,OAAO;AAAA,EACP,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AAAA,EACP,KAAK;AAAA,EACL,YAAY;AACd;AAsCA,MAAM,cAAgD;AAAA,EACpD,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AACP;AAGO,SAAS,yBAAyB,KAAqB;AAC5D,QAAM,QAAQ,IACX,MAAM,QAAQ,EACd,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,EAChC,IAAI,CAAC,SAAS,YAAY,KAAK,aAAa,KAAK,KAAK,aAAa;AACtE,QAAM,CAAC,OAAO,GAAG,IAAI,IAAI;AACzB,MAAI,UAAU,OAAW,QAAO;AAChC,SAAO,CAAC,MAAM,OAAO,CAAC,EAAE,YAAA,IAAgB,MAAM,MAAM,CAAC,GAAG,GAAG,IAAI,EAAE,KAAK,GAAG;AAC3E;AAEA,SAAS,MAAM,KAAa,OAAoC;AAC9D,SAAO,EAAE,KAAK,OAAO,yBAAyB,GAAG,GAAG,MAAA;AACtD;AAGA,SAAS,cAAc,OAAsC;AAC3D,QAAM,SAAgC,CAAA;AACtC,aAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,UAAM,YAAY,KAAK,QAAQ,GAAG;AAClC,QAAI,aAAa,EAAG;AACpB,UAAM,MAAM,KAAK,MAAM,GAAG,SAAS,EAAE,KAAA;AACrC,UAAM,QAAQ,KAAK,MAAM,YAAY,CAAC,EAAE,KAAA;AACxC,QAAI,OAAO,MAAO,QAAO,KAAK,MAAM,KAAK,KAAK,CAAC;AAAA,EACjD;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,WAAW,QAAiC,KAA4B;AAC/E,QAAM,QAAQ,OAAO,GAAG;AACxB,SAAO,OAAO,UAAU,YAAY,MAAM,KAAA,EAAO,SAAS,IAAI,MAAM,KAAA,IAAS;AAC/E;AAEA,SAAS,WAAW,OAA+B;AACjD,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,UAAU;AACtD,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO,OAAO,KAAK;AAC5E,MAAI,OAAO,UAAU,UAAW,QAAO,QAAQ,QAAQ;AACvD,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,QAAQ,MAAM,OAAO,CAAC,SAAyB,OAAO,SAAS,YAAY,KAAK,KAAA,MAAW,EAAE;AACnG,WAAO,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAAA,EAC/C;AACA,SAAO;AACT;AAGA,MAAM,iBAAsC,oBAAI,IAAI,CAAC,WAAW,CAAC;AAG1D,SAAS,mBAAmB,SAAqC;AACtE,SAAO,gBAAgB,QAAQ,KAAA,EAAO,YAAA,CAAa,KAAK,mBAAmB;AAC7E;AAEA,SAAS,SAAS,OAAuB;AACvC,QAAM,QAAQ,MAAM,QAAQ,WAAW,EAAE,EAAE,QAAQ,WAAW,GAAG,EAAE,KAAA;AACnE,SAAO,QAAQ,MAAM,OAAO,CAAC,EAAE,gBAAgB,MAAM,MAAM,CAAC,IAAI;AAClE;AAGO,SAAS,oBAAoB,SAAyB;AAC3D,QAAM,OAAO,mBAAmB,OAAO;AACvC,SAAO,SAAS,mBAAmB,QAAQ,SAAS,OAAO,IAAI,WAAW,IAAI;AAChF;AAMO,SAAS,yBAAyB,OAAuB;AAC9D,QAAM,QAAQ,6BAA6B,KAAK,MAAM,MAAM;AAC5D,SAAO,QAAQ,OAAO,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK;AAC9D;AAEA,MAAM,eAAiD;AAAA,EACrD,kBAAkB;AAAA,EAClB,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,mBAAmB;AACrB;AAEA,MAAM,eAAiD;AAAA,EACrD,UAAU;AAAA,EACV,WAAW;AAAA,EACX,SAAS;AACX;AAEA,SAAS,UAAU,OAAuC,KAA4B;;AACpF,WAAO,WAAM,KAAK,CAAC,UAAU,MAAM,QAAQ,GAAG,MAAvC,mBAA0C,UAAS;AAC5D;AAEA,SAAS,WAAW,OAAsD;AACxE,QAAM,QAAQ,cAAc,KAAK,UAAU,OAAO,eAAe,KAAK,EAAE;AACxE,MAAI,+BAAQ,GAAI,QAAO,MAAM,CAAC;AAC9B,QAAM,OAAO,UAAU,OAAO,oBAAoB;AAClD,SAAO,OAAQ,aAAa,KAAK,YAAA,CAAa,KAAK,SAAS,IAAI,IAAK;AACvE;AAEA,SAAS,WAAW,OAAsD;AACxE,QAAM,OAAO,UAAU,OAAO,yBAAyB,KAAK,UAAU,OAAO,mBAAmB;AAChG,SAAO,OAAQ,aAAa,KAAK,YAAA,CAAa,KAAK,SAAS,IAAI,IAAK;AACvE;AAEA,SAAS,eAAe,OAAuC,SAAgC;AAC7F,QAAM,UACJ,UAAU,OAAO,QAAQ,MAAM,qBAC/B,YAAY,eACZ,YAAY,qBACZ,UAAU,OAAO,iBAAiB,MAAM;AAC1C,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAQ,UAAU,OAAO,aAAa;AAC5C,SAAO,QAAQ,gBAAgB,KAAK,KAAK;AAC3C;AAQO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AACF,GAGiC;AAC/B,QAAM,WAAW,iCAAS;AAC1B,MAAI,CAAC,SAAS,QAAQ,EAAG,QAAO;AAChC,QAAM,UAAU,WAAW,UAAU,SAAS;AAC9C,MAAI,YAAY,KAAM,QAAO;AAE7B,QAAM,UAAiC,CAAA;AACvC,MAAI,SAAS,SAAS,OAAO,GAAG;AAC9B,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,OAAO,GAAG;AAC3D,UAAI,eAAe,IAAI,GAAG,EAAG;AAC7B,YAAM,SAAS,WAAW,KAAK;AAC/B,UAAI,WAAW,KAAM,SAAQ,KAAK,MAAM,KAAK,MAAM,CAAC;AAAA,IACtD;AAAA,EACF;AACA,QAAM,aAAa,WAAW,UAAU,aAAa;AACrD,QAAM,eAAe,WAAW,UAAU,eAAe;AACzD,QAAM,UAAU,WAAW,UAAU,SAAS;AAE9C,QAAM,UAAU,sBAAsB,IAAI;AAC1C,QAAM,QAA+B,UACjC,cAAc,QAAQ,KAAK,IAC3B;AAAA,IACE,MAAM,UAAU,oBAAoB,OAAO,CAAC;AAAA,IAC5C,GAAI,aAAa,CAAC,MAAM,eAAe,UAAU,CAAC,IAAI,CAAA;AAAA,IACtD,GAAI,eAAe,CAAC,MAAM,iBAAiB,YAAY,CAAC,IAAI,CAAA;AAAA,IAC5D,GAAI,UAAU,CAAC,MAAM,WAAW,OAAO,CAAC,IAAI,CAAA;AAAA,IAC5C,GAAG;AAAA,EAAA;AAGT,QAAM,OAAO,mBAAmB,OAAO;AAEvC,QAAM,QAAQ,CAAC,GAAG,OAAO,GAAG,OAAO;AACnC,MAAI,QAAuB;AAC3B,MAAI,SAAS,mBAAmB,MAAO,SAAQ,WAAW,KAAK;AAAA,WACtD,SAAS,mBAAmB,MAAO,SAAQ,WAAW,KAAK;AAAA,WAC3D,SAAS,mBAAmB,IAAK,SAAQ,eAAe,OAAO,OAAO;AAE/E,SAAO;AAAA,IACL,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA,OAAO,oBAAoB,OAAO;AAAA,MAClC;AAAA,MACA,cACE,iBAAiB,QAAQ,SAAS,mBAAmB,MACjD,yBAAyB,YAAY,IACrC;AAAA,MACN;AAAA,MACA;AAAA,IAAA;AAAA,IAEF;AAAA,IACA,aAAY,mCAAS,QAAO;AAAA,IAC5B,QAAQ,UAAU,iBAAiB,OAAO,EAAE,SAAS;AAAA,EAAA;AAEzD;"}
|
|
@@ -7,6 +7,7 @@ const require$$0 = require("react");
|
|
|
7
7
|
const useAthenaRunLive = require("../../chat/use-athena-run-live.cjs");
|
|
8
8
|
const chatMessageBody = require("../rich-text/chat-message-body.cjs");
|
|
9
9
|
const answerLabel = require("./answer-label.cjs");
|
|
10
|
+
const inboundChannelMessage = require("./inbound-channel-message.cjs");
|
|
10
11
|
const messageActionsConfig = require("./message-actions-config.cjs");
|
|
11
12
|
const messageImages = require("./message-images.cjs");
|
|
12
13
|
const runtimeMessage = require("./runtime-message.cjs");
|
|
@@ -18,6 +19,7 @@ const streamingMarkdown = require("../../agent-runtime/src/streaming-markdown.cj
|
|
|
18
19
|
const githubWakeEvent = require("../../agent-runtime/src/github-wake-event.cjs");
|
|
19
20
|
const subscriptionWakeEvent = require("../../agent-runtime/src/subscription-wake-event.cjs");
|
|
20
21
|
const voicePresentation = require("../../agent-runtime/src/voice-presentation.cjs");
|
|
22
|
+
const inboundChannel = require("../../agent-runtime/src/inbound-channel.cjs");
|
|
21
23
|
const composerMarkdown = require("../../agent-runtime/src/composer-markdown.cjs");
|
|
22
24
|
const runtimeMessages = require("../../agent-runtime/src/runtime-messages.cjs");
|
|
23
25
|
const useDeepAgentChatOutbox = require("../../agent-runtime/src/use-deep-agent-chat-outbox.cjs");
|
|
@@ -84,19 +86,17 @@ const MarkdownText = () => {
|
|
|
84
86
|
return /* @__PURE__ */ jsxRuntime.jsx(chatMessageBody.ChatMessageBody, { text, isRunning });
|
|
85
87
|
};
|
|
86
88
|
const UserMessageText = ({ text }) => {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
[composerMarkdown$1, text]
|
|
99
|
-
);
|
|
89
|
+
const custom = react.useAuiState((s) => {
|
|
90
|
+
var _a;
|
|
91
|
+
return (_a = s.message.metadata) == null ? void 0 : _a.custom;
|
|
92
|
+
});
|
|
93
|
+
const shown = require$$0.useMemo(() => {
|
|
94
|
+
const body = inboundChannel.inboundMessageBody({ custom, text });
|
|
95
|
+
return composerMarkdown.readDeepAgentComposerMarkdown({
|
|
96
|
+
metadata: { [composerMarkdown.DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: custom == null ? void 0 : custom[composerMarkdown.DEEP_AGENT_COMPOSER_MARKDOWN_KEY] },
|
|
97
|
+
text: body
|
|
98
|
+
}) ?? body;
|
|
99
|
+
}, [custom, text]);
|
|
100
100
|
return /* @__PURE__ */ jsxRuntime.jsx(chatMessageBody.ChatMessageBody, { text: shown, typed: true });
|
|
101
101
|
};
|
|
102
102
|
const USER_MESSAGE_PART_COMPONENTS = { Text: UserMessageText, Image: messageImages.MessageImagePart };
|
|
@@ -140,7 +140,11 @@ const UserMessage = () => {
|
|
|
140
140
|
"data-message-role": "user",
|
|
141
141
|
className: "group/user-message mb-3 flex justify-end",
|
|
142
142
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 max-w-[85%] flex-col items-end gap-1", children: [
|
|
143
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
143
|
+
/* @__PURE__ */ jsxRuntime.jsx(inboundChannelMessage.InboundChannelLine, {}),
|
|
144
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 max-w-full rounded-2xl rounded-br-md bg-muted px-3.5 py-2 text-sm text-foreground", children: [
|
|
145
|
+
/* @__PURE__ */ jsxRuntime.jsx(inboundChannelMessage.InboundChannelSubject, {}),
|
|
146
|
+
/* @__PURE__ */ jsxRuntime.jsx(messageImages.MessageImageGallery, { children: /* @__PURE__ */ jsxRuntime.jsx(react.MessagePrimitive.Content, { components: USER_MESSAGE_PART_COMPONENTS }) })
|
|
147
|
+
] }),
|
|
144
148
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-6 items-center gap-1", children: [
|
|
145
149
|
/* @__PURE__ */ jsxRuntime.jsx(BranchPicker, {}),
|
|
146
150
|
/* @__PURE__ */ jsxRuntime.jsx(UserActionBar, {})
|